Go -- type 和断言 interface{}转换
摘要
类型转换在程序设计中都是不可避免的问题。当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要 去关注这方面的问题。但是golang中的类型匹配是很严格的,不同的类型之间通常需要手动转换,编译器不会代你去做这个事。我之所以说通常需要手动转 换,是因为interface类型作为一个特例,会有不同的处理方式。
golang中的所有类型都有自己的默认值,对此我做了个测试。
$GOPATH/src
----typeassert_test
--------main.go
main.go的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
<code class = "hljs go" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-keyword" >type myStruct <span class = "hljs-keyword" > struct { name <span class = "hljs-typename" > bool userid <span class = "hljs-typename" >int64 } <span class = "hljs-keyword" >var structZero myStruct <span class = "hljs-keyword" >var intZero <span class = "hljs-typename" > int <span class = "hljs-keyword" >var int32Zero <span class = "hljs-typename" >int32 <span class = "hljs-keyword" >var int64Zero <span class = "hljs-typename" >int64 <span class = "hljs-keyword" >var uintZero <span class = "hljs-typename" >uint <span class = "hljs-keyword" >var uint8Zero <span class = "hljs-typename" >uint8 <span class = "hljs-keyword" >var uint32Zero <span class = "hljs-typename" >uint32 <span class = "hljs-keyword" >var uint64Zero <span class = "hljs-typename" >uint64 <span class = "hljs-keyword" >var byteZero <span class = "hljs-typename" >byte <span class = "hljs-keyword" >var boolZero <span class = "hljs-typename" > bool <span class = "hljs-keyword" >var float32Zero <span class = "hljs-typename" >float32 <span class = "hljs-keyword" >var float64Zero <span class = "hljs-typename" >float64 <span class = "hljs-keyword" >var stringZero <span class = "hljs-typename" >string <span class = "hljs-keyword" >var funcZero <span class = "hljs-keyword" >func(<span class = "hljs-typename" > int ) <span class = "hljs-typename" > int <span class = "hljs-keyword" >var byteArrayZero [<span class = "hljs-number" >5]<span class = "hljs-typename" >byte <span class = "hljs-keyword" >var boolArrayZero [<span class = "hljs-number" >5]<span class = "hljs-typename" > bool <span class = "hljs-keyword" >var byteSliceZero []<span class = "hljs-typename" >byte <span class = "hljs-keyword" >var boolSliceZero []<span class = "hljs-typename" > bool <span class = "hljs-keyword" >var mapZero <span class = "hljs-keyword" >map[<span class = "hljs-typename" >string]<span class = "hljs-typename" > bool <span class = "hljs-keyword" >var interfaceZero <span class = "hljs-keyword" >interface{} <span class = "hljs-keyword" >var chanZero <span class = "hljs-keyword" >chan <span class = "hljs-typename" > int <span class = "hljs-keyword" >var pointerZero *<span class = "hljs-typename" > int <span class = "hljs-keyword" >func main() { fmt.Println(<span class = "hljs-string" > "structZero: " , structZero) fmt.Println(<span class = "hljs-string" > "intZero: " , intZero) fmt.Println(<span class = "hljs-string" > "int32Zero: " , int32Zero) fmt.Println(<span class = "hljs-string" > "int64Zero: " , int64Zero) fmt.Println(<span class = "hljs-string" > "uintZero: " , uintZero) fmt.Println(<span class = "hljs-string" > "uint8Zero: " , uint8Zero) fmt.Println(<span class = "hljs-string" > "uint32Zero: " , uint32Zero) fmt.Println(<span class = "hljs-string" > "uint64Zero: " , uint64Zero) fmt.Println(<span class = "hljs-string" > "byteZero: " , byteZero) fmt.Println(<span class = "hljs-string" > "boolZero: " , boolZero) fmt.Println(<span class = "hljs-string" > "float32Zero: " , float32Zero) fmt.Println(<span class = "hljs-string" > "float64Zero: " , float64Zero) fmt.Println(<span class = "hljs-string" > "stringZero: " , stringZero) fmt.Println(<span class = "hljs-string" > "funcZero: " , funcZero) fmt.Println(<span class = "hljs-string" > "funcZero == nil?" , funcZero == <span class = "hljs-constant" >nil) fmt.Println(<span class = "hljs-string" > "byteArrayZero: " , byteArrayZero) fmt.Println(<span class = "hljs-string" > "boolArrayZero: " , boolArrayZero) fmt.Println(<span class = "hljs-string" > "byteSliceZero: " , byteSliceZero) fmt.Println(<span class = "hljs-string" > "byteSliceZero's len?" , <span class = "hljs-built_in" >len(byteSliceZero)) fmt.Println(<span class = "hljs-string" > "byteSliceZero's cap?" , <span class = "hljs-built_in" >cap(byteSliceZero)) fmt.Println(<span class = "hljs-string" > "byteSliceZero == nil?" , byteSliceZero == <span class = "hljs-constant" >nil) fmt.Println(<span class = "hljs-string" > "boolSliceZero: " , boolSliceZero) fmt.Println(<span class = "hljs-string" > "mapZero: " , mapZero) fmt.Println(<span class = "hljs-string" > "mapZero's len?" , <span class = "hljs-built_in" >len(mapZero)) fmt.Println(<span class = "hljs-string" > "mapZero == nil?" , mapZero == <span class = "hljs-constant" >nil) fmt.Println(<span class = "hljs-string" > "interfaceZero: " , interfaceZero) fmt.Println(<span class = "hljs-string" > "interfaceZero == nil?" , interfaceZero == <span class = "hljs-constant" >nil) fmt.Println(<span class = "hljs-string" > "chanZero: " , chanZero) fmt.Println(<span class = "hljs-string" > "chanZero == nil?" , chanZero == <span class = "hljs-constant" >nil) fmt.Println(<span class = "hljs-string" > "pointerZero: " , pointerZero) fmt.Println(<span class = "hljs-string" > "pointerZero == nil?" , pointerZero == <span class = "hljs-constant" >nil) }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
1
2
3
|
<code class= "hljs bash" >$ <span class= "hljs-built_in" > cd <span class= "hljs-variable" >$GOPATH /src/typeassert_ <span class= "hljs-built_in" > test $ go build $ . /typeassert_ <span class= "hljs-built_in" > test < /span >< /span >< /span >< /span >< /code > |
您可以清楚的了解到各种类型的默认值。如bool的默认值是false,string的默认值是空串,byte的默认值是0,数组的默认就是这个数 组成员类型的默认值所组成的数组等等。然而您或许会发现在上面的例子中:map、interface、pointer、slice、func、chan的 默认值和nil是相等的。关于nil可以和什么样的类型做相等比较,您只需要知道nil可以赋值给哪些类型变量,那么就可以和哪些类型变量做相等比较。官 方对此有明确的说明:http://pkg.golang.org/pkg/builtin/#Type,也可以看我的另一篇文章:golang: 详解interface和nil。所以现在您应该知道nil只能赋值给指针、channel、func、interface、map或slice类型的变量。如果您用int类型的变量跟nil做相等比较,panic会找上您。
对于字面量的值,编译器会有一个隐式转换。看下面的例子:
1
2
3
4
5
6
7
8
9
10
11
12
|
<code class = "hljs go" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-keyword" >func main() { <span class = "hljs-keyword" >var myInt <span class = "hljs-typename" >int32 = <span class = "hljs-number" >5 <span class = "hljs-keyword" >var myFloat <span class = "hljs-typename" >float64 = <span class = "hljs-number" >0 fmt.Println(myInt) fmt.Println(myFloat) }</span></span></span></span></span></span></span></span></span></span></code> |
对于myInt变量,它存储的就是int32类型的5;对于myFloat变量,它存储的是int64类型的0。或许您可能会写出这样的代码,但确实不是必须这么做的:
1
2
3
4
5
6
7
8
9
10
11
12
|
<code class = "hljs go" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-keyword" >func main() { <span class = "hljs-keyword" >var myInt <span class = "hljs-typename" >int32 = <span class = "hljs-typename" >int32(<span class = "hljs-number" >5) <span class = "hljs-keyword" >var myFloat <span class = "hljs-typename" >float64 = <span class = "hljs-typename" >float64(<span class = "hljs-number" >0) fmt.Println(myInt) fmt.Println(myFloat) }</span></span></span></span></span></span></span></span></span></span></span></span></code> |
在C中,大多数类型转换都是可以隐式进行的,比如:
1
2
3
4
5
6
7
8
9
|
<code class = "hljs cpp" ><span class = "hljs-preprocessor" >#<span class = "hljs-keyword" >include <stdio.h> <span class = "hljs-function" ><span class = "hljs-keyword" > int <span class = "hljs-title" >main<span class = "hljs-params" >(<span class = "hljs-keyword" > int argc, <span class = "hljs-keyword" > char **argv) { <span class = "hljs-keyword" > int uid = <span class = "hljs-number" >12345; <span class = "hljs-keyword" > long gid = uid; <span class = "hljs-built_in" > printf (<span class = "hljs-string" > "uid=%d, gid=%d\n" , uid, gid); <span class = "hljs-keyword" > return <span class = "hljs-number" >0; }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
但是在golang中,您不能这么做。有个类似的例子:
1
2
3
4
5
6
7
8
9
10
11
|
<code class = "hljs puppet" ><span class = "hljs-keyword" >package <span class = "hljs-keyword" >main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) func <span class = "hljs-keyword" >main() <span class = "hljs-keyword" >{ var <span class = "hljs-literal" >uid int32 = <span class = "hljs-number" >12345 var <span class = "hljs-built_in" >gid int64 = int64(<span class = "hljs-literal" >uid) fmt.<span class = "hljs-constant" >Printf(<span class = "hljs-string" > "uid=%d, gid=%d\n" , <span class = "hljs-literal" >uid, <span class = "hljs-built_in" >gid) <span class = "hljs-keyword" >}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
很显然,将uid赋值给gid之前,需要将uid强制转换成int64类型,否则会panic。golang中的类型区分静态类型和底层类型。您可以用type关键字定义自己的类型,这样做的好处是可以语义化自己的代码,方便理解和阅读。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<code class = "hljs puppet" ><span class = "hljs-keyword" >package <span class = "hljs-keyword" >main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-built_in" >type <span class = "hljs-constant" >MyInt32 int32 func <span class = "hljs-keyword" >main() <span class = "hljs-keyword" >{ var <span class = "hljs-literal" >uid int32 = <span class = "hljs-number" >12345 var <span class = "hljs-built_in" >gid <span class = "hljs-constant" >MyInt32 = <span class = "hljs-constant" >MyInt32(<span class = "hljs-literal" >uid) fmt.<span class = "hljs-constant" >Printf(<span class = "hljs-string" > "uid=%d, gid=%d\n" , <span class = "hljs-literal" >uid, <span class = "hljs-built_in" >gid) <span class = "hljs-keyword" >}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
在上面的代码中,定义了一个新的类型MyInt32。对于类型MyInt32来说,MyInt32是它的静态类型,int32是它的底层类型。即使 两个类型的底层类型相同,在相互赋值时还是需要强制类型转换的。可以用reflect包中的Kind方法来获取相应类型的底层类型。
对于类型转换的截断问题,为了问题的简单化,这里只考虑具有相同底层类型之间的类型转换。小类型(这里指存储空间)向大类型转换时,通常都是安全的。下面是一个大类型向小类型转换的示例:
1
2
3
4
5
6
7
8
9
10
11
|
<code class = "hljs puppet" ><span class = "hljs-keyword" >package <span class = "hljs-keyword" >main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) func <span class = "hljs-keyword" >main() <span class = "hljs-keyword" >{ var <span class = "hljs-built_in" >gid int32 = <span class = "hljs-number" >0x12345678 var <span class = "hljs-literal" >uid int8 = int8(<span class = "hljs-built_in" >gid) fmt.<span class = "hljs-constant" >Printf(<span class = "hljs-string" > "uid=%#x, gid=%#x\n" , <span class = "hljs-literal" >uid, <span class = "hljs-built_in" >gid) <span class = "hljs-keyword" >}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
在上面的代码中,gid为int32类型,也即占4个字节空间(在内存中占有4个存储单元),因此这4个存储单元的值分别是:0x12, 0x34, 0x56, 0x78。但事实不总是如此,这跟cpu架构有关。在内存中的存储方式分为两种:大端序和小端序。大端序的存储方式是高位字节存储在低地址上;小端序的存 储方式是高位字节存储在高地址上。本人的机器是按小端序来存储的,所以gid在我的内存上的存储序列是这样的:0x78, 0x56, 0x34, 0x12。如果您的机器是按大端序来存储,则gid的存储序列刚好反过来:0x12, 0x34, 0x56, 0x78。对于强制转换后的uid,肯定是产生了截断行为。因为uid只占1个字节,转换后的结果必然会丢弃掉多余的3个字节。截断的规则是:保留低地址 上的数据,丢弃多余的高地址上的数据。来看下测试结果:
1
2
3
4
|
<code class= "hljs bash" >$ <span class= "hljs-built_in" > cd <span class= "hljs-variable" >$GOPATH /src/typeassert_ <span class= "hljs-built_in" > test $ go build $ . /typeassert_ <span class= "hljs-built_in" > test uid=<span class= "hljs-number" >0x78, gid=<span class= "hljs-number" >0x12345678< /span >< /span >< /span >< /span >< /span >< /span >< /code > |
如果您的输出结果是:
1
|
<code class= "hljs nix" ><span class= "hljs-variable" >uid=<span class= "hljs-number" >0x12, <span class= "hljs-variable" >gid=<span class= "hljs-number" >0x12345678< /span >< /span >< /span >< /span >< /code > |
那么请不要惊讶,因为您的机器是属于大端序存储。
其实很容易根据上面所说的知识来判断是属于大端序或小端序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<code class = "hljs go" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-keyword" >func IsBigEndian() <span class = "hljs-typename" > bool { <span class = "hljs-keyword" >var i <span class = "hljs-typename" >int32 = <span class = "hljs-number" >0x12345678 <span class = "hljs-keyword" >var b <span class = "hljs-typename" >byte = <span class = "hljs-typename" >byte(i) <span class = "hljs-keyword" > if b == <span class = "hljs-number" >0x12 { <span class = "hljs-keyword" > return <span class = "hljs-constant" > true } <span class = "hljs-keyword" > return <span class = "hljs-constant" > false } <span class = "hljs-keyword" >func main() { <span class = "hljs-keyword" > if IsBigEndian() { fmt.Println(<span class = "hljs-string" > "大端序" ) } <span class = "hljs-keyword" > else { fmt.Println(<span class = "hljs-string" > "小端序" ) } }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
1
2
3
4
|
<code class= "hljs bash" >$ <span class= "hljs-built_in" > cd <span class= "hljs-variable" >$GOPATH /src/typeassert_ <span class= "hljs-built_in" > test $ go build $ . /typeassert_ <span class= "hljs-built_in" > test 小端序< /span >< /span >< /span >< /span >< /code > |
接口的转换遵循以下规则:
普通类型向接口类型的转换是隐式的。
接口类型向普通类型转换需要类型断言。
普通类型向接口类型转换的例子随处可见,例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
<code class = "hljs scala" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) func main() { <span class = "hljs-keyword" >var <span class = "hljs-function" ><span class = "hljs-keyword" >val<span class = "hljs-title" > interface{} = <span class = "hljs-string" > "hello" fmt.<span class = "hljs-type" >Println(<span class = "hljs-function" ><span class = "hljs-keyword" >val) <span class = "hljs-function" ><span class = "hljs-keyword" >val<span class = "hljs-title" > = []byte{ 'a' , 'b' , 'c' } fmt.<span class = "hljs-type" >Println(<span class = "hljs-function" ><span class = "hljs-keyword" >val) }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
正如您所预料的,"hello"作为string类型存储在interface{}类型的变量val中,[]byte{'a', 'b', 'c'}作为slice存储在interface{}类型的变量val中。这个过程是隐式的,是编译期确定的。
接口类型向普通类型转换有两种方式:Comma-ok断言和switch测试。任何实现了接口I的类型都可以赋值给这个接口类型变量。由于 interface{}包含了0个方法,所以任何类型都实现了interface{}接口,这就是为什么可以将任意类型值赋值给interface{}类 型的变量,包括nil。还有一个要注意的就是接口的实现问题,*T包含了定义在T和*T上的所有方法,而T只包含定义在T上的方法。我们来看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
<code class = "hljs swift" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-comment" > // 演讲者接口 type <span class = "hljs-type" >Speaker interface { <span class = "hljs-comment" > // 说 <span class = "hljs-type" >Say(string) <span class = "hljs-comment" > // 听 <span class = "hljs-type" >Listen(string) string <span class = "hljs-comment" > // 打断、插嘴 <span class = "hljs-type" >Interrupt(string) } <span class = "hljs-comment" > // 王兰讲师 type <span class = "hljs-type" >WangLan <span class = "hljs-class" ><span class = "hljs-keyword" > struct { msg string } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *WangLan) <span class = "hljs-title" >Say<span class = "hljs-params" >(msg string) { fmt.<span class = "hljs-type" >Printf(<span class = "hljs-string" > "王兰说:%s\n" , msg) } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *WangLan) <span class = "hljs-title" >Listen<span class = "hljs-params" >(msg string) <span class = "hljs-title" >string { this .msg = msg <span class = "hljs-keyword" > return msg } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *WangLan) <span class = "hljs-title" >Interrupt<span class = "hljs-params" >(msg string) { this .<span class = "hljs-type" >Say(msg) } <span class = "hljs-comment" > // 江娄讲师 type <span class = "hljs-type" >JiangLou <span class = "hljs-class" ><span class = "hljs-keyword" > struct { msg string } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *JiangLou) <span class = "hljs-title" >Say<span class = "hljs-params" >(msg string) { fmt.<span class = "hljs-type" >Printf(<span class = "hljs-string" > "江娄说:%s\n" , msg) } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *JiangLou) <span class = "hljs-title" >Listen<span class = "hljs-params" >(msg string) <span class = "hljs-title" >string { this .msg = msg <span class = "hljs-keyword" > return msg } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-params" >( this *JiangLou) <span class = "hljs-title" >Interrupt<span class = "hljs-params" >(msg string) { this .<span class = "hljs-type" >Say(msg) } <span class = "hljs-func" ><span class = "hljs-keyword" >func <span class = "hljs-title" >main<span class = "hljs-params" >() { wl := &<span class = "hljs-type" >WangLan{} jl := &<span class = "hljs-type" >JiangLou{} <span class = "hljs-keyword" >var person <span class = "hljs-type" >Speaker person = wl person.<span class = "hljs-type" >Say(<span class = "hljs-string" > "Hello World!" ) person = jl person.<span class = "hljs-type" >Say(<span class = "hljs-string" > "Good Luck!" ) }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
Speaker接口有两个实现WangLan类型和JiangLou类型。但是具体到实例来说,变量wl和变量jl只有是对应实例的指针类型才真正 能被Speaker接口变量所持有。这是因为WangLan类型和JiangLou类型所有对Speaker接口的实现都是在*T上。这就是上例中 person能够持有wl和jl的原因。
想象一下java的泛型(很可惜golang不支持泛型),java在支持泛型之前需要手动装箱和拆箱。由于golang能将不同的类型存入到接口 类型的变量中,使得问题变得更加复杂。所以有时候我们不得不面临这样一个问题:我们究竟往接口存入的是什么样的类型?有没有办法反向查询?答案是肯定的。
Comma-ok断言的语法是:value, ok := element.(T)。element必须是接口类型的变量,T是普通类型。如果断言失败,ok为false,否则ok为true并且value为变量的值。来看个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<code class = "hljs cs" ><span class = "hljs-function" >package main <span class = "hljs-title" >import <span class = "hljs-params" >( <span class = "hljs-string" > "fmt" ) type Html []<span class = "hljs-keyword" >interface{} <span class = "hljs-function" >func <span class = "hljs-title" >main<span class = "hljs-params" >() { html := make(Html, <span class = "hljs-number" >5) html[<span class = "hljs-number" >0] = <span class = "hljs-string" > "div" html[<span class = "hljs-number" >1] = <span class = "hljs-string" > "span" html[<span class = "hljs-number" >2] = []<span class = "hljs-keyword" >byte(<span class = "hljs-string" > "script" ) html[<span class = "hljs-number" >3] = <span class = "hljs-string" > "style" html[<span class = "hljs-number" >4] = <span class = "hljs-string" > "head" <span class = "hljs-keyword" > for index, element := range html { <span class = "hljs-keyword" > if <span class = "hljs-keyword" >value, ok := element.(<span class = "hljs-keyword" >string); ok { fmt.Printf(<span class = "hljs-string" > "html[%d] is a string and its value is %s\n" , index, <span class = "hljs-keyword" >value) } <span class = "hljs-keyword" > else <span class = "hljs-keyword" > if <span class = "hljs-keyword" >value, ok := element.([]<span class = "hljs-keyword" >byte); ok { fmt.Printf(<span class = "hljs-string" > "html[%d] is a []byte and its value is %s\n" , index, <span class = "hljs-keyword" >string(<span class = "hljs-keyword" >value)) } } }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
其实Comma-ok断言还支持另一种简化使用的方式:value := element.(T)。但这种方式不建议使用,因为一旦element.(T)断言失败,则会产生运行时错误。如:
1
2
3
4
5
6
7
8
9
10
11
|
<code class = "hljs scala" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) func main() { <span class = "hljs-keyword" >var <span class = "hljs-function" ><span class = "hljs-keyword" >val<span class = "hljs-title" > interface{} = <span class = "hljs-string" > "good" fmt.<span class = "hljs-type" >Println(<span class = "hljs-function" ><span class = "hljs-keyword" >val.(string)) <span class = "hljs-comment" > // fmt.Println(val.(int)) }</span></span></span></span></span></span></span></span></span></span></span></span></code> |
以上的代码中被注释的那一行会运行时错误。这是因为val实际存储的是string类型,因此断言失败。
还有一种转换方式是switch测试。既然称之为switch测试,也就是说这种转换方式只能出现在switch语句中。可以很轻松的将刚才用Comma-ok断言的例子换成由switch测试来实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<code class = "hljs go" ><span class = "hljs-keyword" >package main <span class = "hljs-keyword" >import ( <span class = "hljs-string" > "fmt" ) <span class = "hljs-keyword" >type Html []<span class = "hljs-keyword" >interface{} <span class = "hljs-keyword" >func main() { html := <span class = "hljs-built_in" >make(Html, <span class = "hljs-number" >5) html[<span class = "hljs-number" >0] = <span class = "hljs-string" > "div" html[<span class = "hljs-number" >1] = <span class = "hljs-string" > "span" html[<span class = "hljs-number" >2] = []<span class = "hljs-typename" >byte(<span class = "hljs-string" > "script" ) html[<span class = "hljs-number" >3] = <span class = "hljs-string" > "style" html[<span class = "hljs-number" >4] = <span class = "hljs-string" > "head" <span class = "hljs-keyword" > for index, element := <span class = "hljs-keyword" >range html { <span class = "hljs-keyword" > switch value := element.(<span class = "hljs-keyword" >type) { <span class = "hljs-keyword" > case <span class = "hljs-typename" >string: fmt.Printf(<span class = "hljs-string" > "html[%d] is a string and its value is %s\n" , index, value) <span class = "hljs-keyword" > case []<span class = "hljs-typename" >byte: fmt.Printf(<span class = "hljs-string" > "html[%d] is a []byte and its value is %s\n" , index, <span class = "hljs-typename" >string(value)) <span class = "hljs-keyword" > case <span class = "hljs-typename" > int : fmt.Printf(<span class = "hljs-string" > "invalid type\n" ) <span class = "hljs-keyword" > default : fmt.Printf(<span class = "hljs-string" > "unknown type\n" ) } } }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code> |
1
2
3
|
<code class= "hljs bash" >$ <span class= "hljs-built_in" > cd <span class= "hljs-variable" >$GOPATH /src/typeassert_ <span class= "hljs-built_in" > test $ go build $ . /typeassert_ <span class= "hljs-built_in" > test < /span >< /span >< /span >< /span >< /code > |
Go -- type 和断言 interface{}转换的更多相关文章
- golang type 和断言 interface{}转换
摘要 类型转换在程序设计中都是不可避免的问题.当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要去关 注这方面的问题.但是golang中的类型匹配是很严格的,不同的类型之间通常需要手动转换,编 ...
- is not valid JSON: json: cannot unmarshal string into Go value of type map[string]interface | mongodb在windows和Linux导出出错
执行mongoexport命令的时候 mongoexport --csv -f externalSeqNum,paymentId --host 127.0.0.1:27017 -d liveX -c ...
- 工作随笔——Golang interface 转换成其他类型
新的公司,新的氛围.一年了,打算写点什么.so,那就写google的golang语言吧. 最最最基础的语法结构见go语言菜鸟教程 接下来写点菜鸟教程没有的. go语言的设计者认为:go语言必须让程序员 ...
- 【转】GO语言map类型interface{}转换踩坑小记
原文:https://www.az1314.cn/art/69 ------------------------------------------ mapA := make([string]inte ...
- TypeScript: type alias 与 interface
官方文档中有关于两者对比的信息,隐藏在 TypeScript Handbook 中,见 Interfaces vs. Type Aliases 部分. 但因为这一部分很久没更新了,所以其中描述的内容不 ...
- Golang 类型转换,断言和显式强制转换
1 前言 类型转换,可以用断言(只能使用在interface{}类型转换成其它类型)和显式类型强制转换(常规是用于基本类型) 2 代码 //graphql-go func(params graphql ...
- Using an Interface as a Type
When you define a new interface, you are defining a new reference data type. You can use interface n ...
- 【区分】Typescript 中 interface 和 type
在接触 ts 相关代码的过程中,总能看到 interface 和 type 的身影.只记得,曾经遇到 type 时不懂查阅过,记得他们很像,相同的功能用哪一个都可以实现.但最近总看到他们,就想深入的了 ...
- Go语言学习笔记(四)结构体struct & 接口Interface & 反射
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...
随机推荐
- 四:SQL语句介绍
前言:介绍SQL语句及其大致的分类 一:SQL语句介绍(Structured SQL Lanage) 结构化的查询语言 是一种特殊的编程语言 是一种数据库查询和程序设计语言 用于存取数据及查询.更新和 ...
- Python内置方法详解
1. 字符串内置方法详解 为何要有字符串?相对于元组.列表等,对于唯一类型的定义,字符串具有最简单的形式. 字符串往往以变量接收,变量名. 可以查看所有的字符串的内置方法,如: 1> count ...
- 【bug】 1118 Row size too large
1118 Row size too large Every table (regardless of storage engine) has a maximum row size of 65,535 ...
- Many-to-one
创建模型 from django.db import models class Reporter(models.Model): first_name = models.CharField(max_le ...
- Python中sys.argv的用法
sys.argv是获取运行python文件的时候命令行参数 下面的代码文件是a.py,当我不用IDE工具,只用命令行窗口运行的时候,进入文件所在目录,输入:python a.py 输出结果如下 imp ...
- python中split()函数讲解
本文讲述的是string.split(s[, sep[, maxsplit]]),针对string类型的split()函数.它主要是切割字符串,结果返回由字符串元素组成的一个列表,具体怎么使用看下面的 ...
- 【MySQL】Got fatal error 1236原因和解决方法
一 前言 MySQL 的主从复制作为一项高可用特性,用于将主库的数据同步到从库,在维护主从复制数据库集群的时候,作为专职的MySQL DBA,笔者相信大多数人都会遇到“Got fatal error ...
- php在字符串中替换多个字符
php替换多个字符串str_replace函数 echo str_replace(array("m","i"),array("n",&quo ...
- c标准库 徐明远 背景基础
背景基础 1.c语言库用c语言编写 其他语言则不同 早期语言的库是用汇编语言编写的 不同的计算机体系结构有不同的汇编语言 所以在移植性方面差一点 而c语言可以编写出高度可移植性的代码 ...
- Android ListView子item高度定长固定值无效问题
在项目开发中遇到一个实际问题:ListView中,打算给每个子item设定一个具体的高度值如128dp,256dp,在子item中把根布局的高度值写死写成定长,但是不管怎么样,高度一直都没变化.开始以 ...