因为对表达式断言和赋值断言的理解不够清晰,所以写了这篇博客来加深理解。

为了更好地理解类型断言的例子,后面的内容都遵循以下惯例:

  • 大写 I:表示接口类型;小写 i:表示接口值。
  • 大写 S:表示具体类型;小写 s:表示具体类型值。
  • 大写 T:表示具体类型和接口类型其中一种;小写 t:表示具体类型值和接口值其中一种。

表达式断言

表达式断言的语法为:i.(T),其中 i 为接口值,T 为具体类型或接口类型。发生错误时,为运行时错误。

当 T 为具体类型时,作用是把接口值转换为具体类型值

当 i 的具体类型是 T 时,断言成功:

1
2
3
4
5
type I interface{}
type S struct{}
var i I = S{}
_ = i.(S)
// i 的具体类型是 S,所以断言成功。

表达式返回一个新值,该值为具体类型值,类型为 T。

当 i 的具体类型不是 T 时,断言失败:

1
2
3
4
5
type I interface{}
type S struct{}
var i I
_ = i.(S)
// i 的具体类型未声明,不是 S,所以断言失败。

当 T 为接口类型时,作用是转换接口类型

当 i 的具体类型实现了 T 接口时,断言成功:

1
2
3
4
5
6
type I1 interface{}
type I2 interface{}
type S struct{}
var i I1 = S{}
_ = i.(I2)
// i 的具体类型 S 实现了 I2 接口,所以断言成功。i 的接口类型从 I1 变为 I2。

表达式返回一个新值,该值为接口值,类型为 T。接口值里面的具体类型和具体值不变。

当 i 的具体类型未实现 T 接口时,断言失败:

1
2
3
4
5
6
type I1 interface{}
type I2 interface{ M() }
type S struct{}
var i I1 = S{}
_ = i.(I2)
// i 的具体类型 S 没有实现 M() 方法,即没实现 I2 接口,所以断言失败。

赋值断言

赋值断言的语法为:i = t,其中 i 为接口值,t 为具体类型值或接口值。发生错误时,为编译时错误。

当 t 为具体类型值时,作用是转换具体类型

当 t 的具体类型实现了 i 的接口时,断言成功:

1
2
3
4
5
6
7
type I interface{}
type S1 struct{}
type S2 struct{}
t := S1{}
var i I = S2{}
i = t
// t 的具体类型 S1 实现了 I 接口,所以断言成功。i 的具体类型从 S2 变为 S1。

i 的具体类型变为 t 的具体类型,接口类型保持不变。

当 t 的具体类型未实现 i 的接口时,断言失败:

1
2
3
4
5
6
7
8
9
type I interface{ M() }
type S1 struct{}
type S2 struct{}
func (S2) M() {}

t := S1{}
var i I = S2{}
i = t
// t 的具体类型 S1 没有实现 M() 方法,即没实现 I 接口,所以断言失败。

当 t 为接口值时,作用是转换接口类型

当 t 的具体类型实现了 i 的接口时,断言成功:

1
2
3
4
5
6
7
8
type I1 interface{}
type I2 interface{}
type S1 struct{}
type S2 struct{}
var t I1 = S1{}
var i I2 = S2{}
i = t
// t 的具体类型 S1 实现了 i 的接口 I2,i 的接口类型从 I2 变为 I1。

i 的接口类型变为 t 的接口类型,具体类型保持不变。显示转换也可以达到同样的作用,如:i = io.Writer(t)

当 t 的具体类型未实现 i 的接口时,断言失败:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type I1 interface{}
type I2 interface{ M() }
type S1 struct{}
type S2 struct{}
func (S2) M() {}

var t I1 = S1{}
var i I2 = S2{}
i = t
// t 的具体类型 S1 未实现 i 的接口 I2,所以断言失败。