interfaz vacía
definición
La interfaz vacía es una forma especial del tipo de interfaz. Las interfaces ordinarias tienen métodos, pero las interfaces vacías no definen ningún puerto de método. Por lo tanto, podemos decir que todos los tipos implementan al menos interfaces vacías.
type test interface {
}
复制代码
Cada interfaz contiene dos propiedades, una es el valor y otra es el tipo.
var i interface{}
fmt.Printf("类型:%T----值:%v\n", i, i) //类型:<nil>----值:<nil>
复制代码
Se puede ver que para las interfaces vacías, ambas son nulas
Escenas a utilizar
Primero , generalmente declaramos una instancia directamente usando interface{}
como tipo, y esta instancia puede contener valores de cualquier tipo.
func main() {
var i interface{}
i = 100
fmt.Println(i) //100
i = "yif"
fmt.Println(i) //yif
i = 3.14
fmt.Println(i) //3.14
i = false
fmt.Println(i) //false
}
复制代码
En segundo lugar , si desea que su función acepte cualquier tipo de valor, también puede usar una interfaz vacía. El siguiente código se imprime normalmente:
func main() {
i := 100
s := "yif"
f := 3.14
test(i)
test(s)
test(f)
}
func test(i interface{}) {
fmt.Println(i)
}
复制代码
La forma de escribir anterior es un poco engorrosa, puede usar funciones variádicas. como sigue:
func main() {
i := 100
s := "yif"
f := 3.14
test(i, s, f)
}
func test(res ...interface{}) {
fmt.Println(res) //res是切片
for k, v := range res {
fmt.Println(k, v)
}
}
复制代码
resultado:
D:\workspace\go\src\test>go run main.go
[100 yif 3.14]
0 100
1 yif
2 3.14
复制代码
Tercero , también define una matriz, sector, mapa, cadena que puede recibir cualquier tipo, por ejemplo, defina un sector aquí
func main() {
sli := make([]interface{}, 4)
sli[0] = 100
sli[1] = "yif"
sli[2] = []int{1, 2, 3}
sli[3] = [...]int{5, 6, 7}
fmt.Println(sli)
for k, v := range sli {
fmt.Println(k, v)
}
}
复制代码
resultado:
D:\workspace\go\src\test>go run main.go
[100 yif [1 2 3] [5 6 7]]
0 100
1 yif
2 [1 2 3]
3 [5 6 7]
复制代码
Varios pozos a los que prestar atención en la interfaz vacía.
**Primero,** una interfaz vacía puede tener cualquier valor, pero eso no significa que cualquier tipo pueda aceptar el valor del tipo de interfaz vacía.
Un tipo de interfaz vacío puede contener cualquier valor y también puede recuperar el valor original de la interfaz vacía.
Pero si asigna un objeto de tipo de interfaz vacío a un objeto de tipo fijo (como int, cadena, etc.), se informará un error.
var i interface{} = 100
var t int = i // cannot use i (type interface {}) as type int in assignment: need type assertion
复制代码
Pero puedes usar declaraciones de variables cortas:
var i interface{} = 100
t := i
fmt.Println(t) //100
复制代码
Porque el compilador deducirá el tipo de la variable según el valor del lado derecho del signo igual para completar la inicialización.
**Segundo:** Cuando la interfaz vacía contiene matrices y sectores, el objeto ya no se puede dividir
sli := []int{1, 2, 3, 4}
var i interface{}
i = sli
fmt.Println(i[1:2]) //cannot slice i (type interface {})
复制代码
tipo aserción
La aserción de tipo es una operación que se utiliza en los valores de interfaz para verificar si el valor que tiene una variable de tipo de interfaz implementa la interfaz esperada o el tipo concreto.
Aserción de tipo, solo se pueden afirmar los objetos cuyo tipo estático es una interfaz vacía (interfaz{}); de lo contrario, se generará un error
Dos sintaxis para la aserción de tipo en Go
La primera sintaxis para aserciones de tipo en Go es la siguiente:
t := i.(T)
复制代码
Esta expresión puede afirmar que un objeto de interfaz (i) no es nulo, y el tipo de valor almacenado en el objeto de interfaz (i) es T. Si la afirmación tiene éxito, devolverá el valor a t, y si la afirmación falla , se activará el pánico.
func main() {
var i interface{} = 100
t := i.(int)
fmt.Println(t) //100
fmt.Println("------------------------------------")
s := i.(string)
fmt.Println(s)
}
复制代码
Resultado [Error al ejecutar la segunda aserción y pánico activado]:
D:\workspace\go\src\test>go run main.go
100
------------------------------------
panic: interface conversion: interface {} is int, not string
goroutine 1 [running]:
main.main()
D:/workspace/go/src/test/main.go:32 +0x10e
exit status 2
复制代码
如果要断言的接口值是 nil,那我们来看看也是不是也如预期一样会触发panic
var i interface{}
var _ = i.(interface{})
复制代码
结果:
D:\workspace\go\src\test>go run main.go
panic: interface conversion: interface is nil, not interface {}
goroutine 1 [running]:
main.main()
D:/workspace/go/src/test/main.go:27 +0x34
exit status 2
复制代码
在Go语言中类型断言的另一种语法格式如下:
t, ok:= i.(T)
复制代码
和上面一样,这个表达式也是可以断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言成功,就会返回其类型给 t,并且此时 ok 的值 为 true,表示断言成功。
如果接口值的类型,并不是我们所断言的 T,就会断言失败,但和第一种表达式不同的事,这个不会触发 panic,而是将 ok 的值设为 false ,表示断言失败,此时t 为 T 的零值。
func main() {
var i interface{} = 10
t1, ok := i.(int)
fmt.Printf("%d-%t\n", t1, ok)
fmt.Println("=====分隔线1=====")
t2, ok := i.(string)
fmt.Printf("%s-%t\n", t2, ok)
fmt.Println("=====分隔线2=====")
var k interface{} // nil
t3, ok := k.(interface{})
fmt.Println(t3, "-", ok)
fmt.Println("=====分隔线3=====")
k = 10
t4, ok := k.(interface{})
fmt.Printf("%d-%t\n", t4, ok)
t5, ok := k.(int)
fmt.Printf("%d-%t\n", t5, ok)
}
复制代码
结果【运行后输出如下,可以发现在执行第二次断言的时候,虽然失败了,但并没有触发了 panic】:
D:\workspace\go\src\test>go run main.go
10-true
=====分隔线1=====
-false
=====分隔线2=====
<nil> - false
=====分隔线3=====
10-true
10-true
复制代码
上面这段输出,你要注意的是第二个断言的输出在-false
之前并不是有没有输出任何 t2 的值,而是由于断言失败,所以 t2 得到的是 string 的零值也是 ""
,它是零长度的,所以你看不到其输出。
类型断言配合 switch 使用
如果需要区分多种类型,可以使用 type switch 断言。
func main() {
test(100)
test("yif")
test(3.14)
var i interface{} //nil
test(i)
test(nil)
}
func test(i interface{}) {
switch r := i.(type) {
case int:
fmt.Println(r, "是int型")
case string:
fmt.Println(r, "是字符串")
case nil:
fmt.Println(r, "是nil")
default:
fmt.Println("没有结果!")
}
}
复制代码
结果:
D:\workspace\go\src\test>go run main.go
100 是int型
yif 是字符串
没有结果!
<nil> 是nil
<nil> 是nil
复制代码