Comment on page
12 空接口(Empty Interface)
由此得出的结论是,每种类型都符合
interface{}
。在实践中,一般认为空接口就是Java或C#中的
object
类型的Go语言版,因为它结合了类型和类型的值。空接口实际上是静态语言中的一种动态类型。
空接口也是在Go程序中实现联合体(
union
)的一种方式。由于每种类型都适配
interface{}
,所以您可以把任意类型的值赋给interface{}
类型的变量。这时,您就不能在编译时确定变量的真正类型。基本例子:
Go
Output
package main
import "fmt"
func printVariableType(v interface{}) {
switch v.(type) {
case string:
fmt.Printf("v is of type 'string'\n")
case int:
fmt.Printf("v is of type 'int'\n")
default:
// generic fallback
fmt.Printf("v is of type '%T'\n", v)
}
}
func main() {
printVariableType("string") // string
printVariableType(5) // int
printVariableType(int32(5)) // int32
}
v is of type 'string'
v is of type 'int'
v is of type 'int32'
在编译时,如果您拿到了接口(包括空接口)类型的数据,那您并不知道它真实的、底层的数据类型。
但您可以在运行时,通过类型断言获取其底层类型。
Go
Output
package main
import "fmt"
func printTypeAndValue(iv interface{}) {
if v, ok := iv.(string); ok {
fmt.Printf("iv is of type string and has value '%s'\n", v)
return
}
if v, ok := iv.(int); ok {
fmt.Printf("iv is of type int and has value '%d'\n", v)
return
}
if v, ok := iv.(*int); ok {
fmt.Printf("iv is of type *int and has value '%p'\n", v)
return
}
}
func panicOnInvalidConversion() {
var iv interface{} = "string"
v := iv.(int)
fmt.Printf("v is int of value: %d\n", v)
}
func main() {
// pass a string
printTypeAndValue("string")
i := 5
// pass an int
printTypeAndValue(i)
// pass a pointer to int i.e. *int
printTypeAndValue(&i)
panicOnInvalidConversion()
}
iv is of type string and has value 'string'
iv is of type int and has value '5'
iv is of type *int and has value '0xc00002c008'
panic: interface conversion: interface {} is string, not int
goroutine 1 [running]:
main.panicOnInvalidConversion()
/tmp/sandbox390319121/prog.go:23 +0x45
main.main()
/tmp/sandbox390319121/prog.go:36 +0x9a
Program exited: status 2.
类型断言使得您能够检查空接口的值是否是某个给定的类型。
为了语法的完整性,类型交换机(type switch)有简短版本:
v := iv.(int)
(或者v, ok := iv.(int)
)。这又称为类型断言。跟完整版的类型交换机不同的是,如果您所断言的类型与变量的实际类型不符时,简短版将会发生恐慌(panic)。
Go
Output
func panicOnInvalidConversion(iv interface{}) {
v := iv.(int)
fmt.Printf("v is int of value: %d\n", v)
}
func main() {
panicOnInvalidConversion("string")
}
panic: interface conversion: interface {} is string, not int
goroutine 1 [running]:
main.panicOnInvalidConversion(0x4a0b40, 0x4dae60)
/tmp/sandbox387618651/prog.go:6 +0xd6
main.main()
/tmp/sandbox387618651/prog.go:11 +0x39
Program exited: status 2.
根据经验,您不应该尝试去发现接口类 型的底层值,因为这样做刺破了抽象层。
关于Switch本词的译法探讨,我们先看看英文语境中有哪些东西叫switch。
例如电路开关:控制电流应通向哪条线路(断路也是线路之一);轨道转辙器:控制铁轨岔口向哪个方向联通;网络交换机:控制输入的数据报文应该由哪个端口输出……
可见,Switch的原生语义逻辑是指,一个系统中流动的对象(如电路中的电流、铁轨上的火车、互联网上的数据报文),当流经“switch”时,“switch”负责把它导往合适的去向。
而中英词典中对switch的中文译法只有:开关、转换、调换、交换机、(轨道)转辙器。
此处,感觉只有“类型交换机”更贴切,毕竟大家都熟知网络交换机的作用,应当不会理解为“换位置、做交易”之类的交换。而“类型开关、类型转换、类型调换”相去甚远了。
switch语句可以根据被接口包装的值得实际类型进行派发。
Go
Output
package main
import (
"fmt"
"strconv"
)
func smartConvertToInt(iv interface{}) (int, error) {
// inside case statements, v is of type matching case type
switch v := iv.(type) {
case int:
return v, nil
case string:
return strconv.Atoi(v)
case float64:
return int(v), nil
default:
return 0, fmt.Errorf("unsupported type: %T", iv)
}
}
func printSmartConvertToInt(iv interface{}) {
i, err := smartConvertToInt(iv)
if err != nil {
fmt.Printf("Failed to convert %#v to int\n", iv)
return
}
fmt.Printf("%#v of type %T converted to %d\n", iv, iv, i)
}
func main() {
printSmartConvertToInt("5")
printSmartConvertToInt(4)
printSmartConvertToInt(int32(8))
printSmartConvertToInt("not valid int")
}
"5" of type string converted to 5
4 of type int converted to 4
Failed to convert 8 to int
Failed to convert "not valid int" to int
Program exited.
最近更新 3yr ago