Go의 인터페이스는 그 안에 담긴 실제 타입이 무엇인지 모호하게 만들 수 있어요. 실제로 빈 인터페이스는 어떤 구체 값이든 담을 수 있죠(기본 타입까지도요). 타입 단언을 사용하면 interfaceVariable.(concreteType) 문법으로 인터페이스 값이 품고 있는 실제 구체 값을 꺼낼 수 있어요.
이 문장의 결과를 값 하나에 대입하면, 그 인터페이스 변수가 해당 구체 타입의 값을 담고 있다고 단언하는 셈이에요. 따라서 인터페이스 값이 지정한 구체 타입이 아니라면 런타임에 패닉이 발생할 수 있어요. 예를 들어:
var input interface{} = 12
str := input.(string) // panic at runtime since input is not a string!
타입 단언의 두 반환 값, 즉 실제 값과 단언이 성공했는지를 알려 주는 불리언 값을 모두 활용하면 인터페이스 값이 특정 구체 타입을 담고 있는지 검사할 수 있어요. 예를 들어:
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)
}