类型转换和类型断言是Go语言中比较绕的地方.

const

不过Go语言必须做显示的类型转换的要求也有例外的情况:

TIIXI

类型之间转换的例子

下面是Go语言规范给出的部分例子:

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point
<-chan int(c)    // same as <-(chan int(c))
(<-chan int)(c)  // c is converted to <-chan int
func()(x)        // function signature func() x
(func())(x)      // x is converted to func()
(func() int)(x)  // x is converted to func() int
func() int(x)    // x is converted to func() int (unambiguous)
xTT(x)()
(func() int)(x)
<-chan intchan<- int

接口之间转换的例子

Go语言中接口的类型转换有很多奇怪的特性: 有时候是隐式转换, 有时候需要类型断言.

Go语言的接口之间虽然也有强制转换的语法, 但是因为接口间支持隐式转换, 因此接口之间
的强制转换语法只是一个摆设.

比如有以下2个接口类型:

type IA interface {}
type IB interface {Foo()}
IAIB
var a A
var b = a.(B)
anila.(B)
IBIA
var b B
var a = A(b)

前面我们说过, Go语言的接口是隐式转换的, 因此还可以省略强制转换的语句:

var b B
var a = b

接口和类型之间的转换例子

虽然前面看到接口之间偶尔也会有类似普通类型之间的强制强制转换语法,
但从本意上来说接口是一个特殊的类型(和普通的类型区别).

IAIB
type TA int
type TB int
func (TB) Foo() {}
TATBTATBIAIB

普通类型向接口类型转换是隐式的(可以编译期确定, 接口的隐式转换特权):

var ta TA
var ia = ta
var tb TB
var ib = tb

接口类型向普通类型是类型断言(运行期确定):

var ia IA
var ta = ia.(TA)
var ib IB
var tb = ib.(TB)

类型断言在编译期是没有任何保障的, 错误的代码也可以编译通过:

var ta = ib.(TA)
var tb = ia.(TB)

总结

因为Go语言的类型转换和类型断言设计的不够完美, 因此很难简单归纳.

下面是我整理的判断是类型转换还是类型断言的伪代码:

func x 转换为 y
    if x 是接口吗 ? {
        if 可以编译期转换吗 ? {
            这是类型转换 (接口之间可以隐式转换)
            // 这里也可以用类型断言, 如果编译期不优化的效率可能低一些
        } else {
            这是类型断言 (运行时也可能会失败)
        }
    } else {
        if 可以编译期转换 ? {
            这是类型转换 (显示转换, 必须成功, 但可能会丢失数据)
        } else {
            禁止!
        }
    }
}

我们可以看到, 当接口之间可以用类型转换的时候, 其实也是可以用类型断言的.

Go语言接口之间的转换是最混乱的特征之一. 期望Go2.0能够有所改善.