Golang empty interface and type assertion

empty interface

definition

Empty interface is a special form of interface type. Ordinary interfaces have methods, but empty interfaces do not define any method ports. Therefore, we can say that all types implement at least empty interfaces.

type test interface {
}
复制代码

Each interface contains two properties, one is the value and one is the type.

var i interface{}
fmt.Printf("类型:%T----值:%v\n", i, i) //类型:<nil>----值:<nil>
复制代码

It can be seen that for empty interfaces, both are nil

scenes to be used

First , we usually declare an instance directly using interface{}as a type, and this instance can hold values ​​of any type.

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
}
复制代码

Second , if you want your function to accept any type of value, you can also use an empty interface. The following code prints normally:

func main() {
	i := 100
	s := "yif"
	f := 3.14

	test(i)
	test(s)
	test(f)
}

func test(i interface{}) {
	fmt.Println(i)
}
复制代码

The above way of writing is a bit cumbersome, you can use variadic functions. as follows:

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)
	}
}
复制代码

result:

D:\workspace\go\src\test>go run main.go
[100 yif 3.14]
0 100
1 yif
2 3.14 
复制代码

Third , you also define an array, slice, map, strcut that can receive any type, for example, define a slice here

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)
	}
}
复制代码

result:

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] 
复制代码

Several pits to pay attention to in the empty interface

**First,** an empty interface can carry any value, but it does not mean that any type can accept the value of the empty interface type

An empty interface type can hold any value, and can also retrieve the original value from the empty interface.

But if you assign an object of empty interface type to an object of a fixed type (such as int, string, etc.), an error will be reported.

var i interface{} = 100
var t int = i // cannot use i (type interface {}) as type int in assignment: need type assertion
复制代码

But you can use short variable declarations:

var i interface{} = 100
t := i
fmt.Println(t) //100
复制代码

Because the compiler will deduce the type of the variable based on the value on the right side of the equal sign to complete the initialization.

**Second:** When the empty interface carries arrays and slices, the object can no longer be sliced

sli := []int{1, 2, 3, 4}
var i interface{}
i = sli
fmt.Println(i[1:2]) //cannot slice i (type interface {})
复制代码

type assertion

Type Assertion is an operation used on interface values ​​to check whether the value held by an interface type variable implements the expected interface or concrete type

Type assertion, only objects whose static type is an empty interface (interface{}) can be asserted, otherwise an error will be thrown

Two syntaxes for type assertion in Go

The first syntax for type assertions in Go is as follows:

t := i.(T)
复制代码

This expression can assert that an interface object (i) is not nil, and the type of the value stored in the interface object (i) is T. If the assertion succeeds, it will return the value to t, and if the assertion fails, panic will be triggered.

func main() {
	var i interface{} = 100
	t := i.(int)
	fmt.Println(t) //100
	
	fmt.Println("------------------------------------")

	s := i.(string)
	fmt.Println(s)
}
复制代码

Result [Failed when executing the second assertion and triggered panic]:

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
复制代码

Guess you like

Origin juejin.im/post/7078550092419956749