Goのインターフェースは、その中にある型についてあいまいさを持ち込むことがあります。実際、空のインターフェースはどんな具体的な値でも(プリミティブ型も含めて)受け取ることができます。型アサーションを使うと、interfaceVariable.(concreteType)という構文で、インターフェース値の中にある具体的な値を取り出せます。
この式の結果を1つの値に代入すると、そのインターフェース変数が指定した具体的な型の値を持っていると表明したことになります。そのため、インターフェース値が指定した具体的な型を持っていなければ、実行時にパニックを起こす可能性があります。たとえば次のようになります。
var input interface{} = 12
str := input.(string) // panic at runtime since input is not a string!
型アサーションの2つの戻り値、つまり中にある値と、アサーションが成功したかどうかを示す真偽値の両方を使うと、インターフェース値が特定の具体的な型を持っているかどうかを調べられます。たとえば次のようになります。
str, ok := input.(string) // no panic if input is not a string
inputがstringを持っていれば、strにはその中にある値が入り、okはtrueになります。inputがstringを持っていなければ、strはstring型のゼロ値(つまり""、空文字列)になり、okはfalseになります。どちらの場合もパニックは起きません。
よく見かけるのは、次のようなイディオムです。
str, ok := input.(string)
if !ok {
str = "a default value"
}
型スイッチを使うと、複数の型アサーションを連続して行えます。構文は型アサーション(interfaceVariable.(concreteType))と同じですが、具体的なconcreteTypeの代わりにtypeというキーワードを使います。たとえば次のようになります。
var i interface{} = 12 // try: 12.3, true, int64(12), []int{}, map[string]int{}
switch v := i.(type) {
case int:
fmt.Printf("the integer %d\n", v)
case string:
fmt.Printf("the string %s\n", v)
default:
fmt.Printf("type, %T, not handled explicitly: %#v\n", v, v)
}