摘要
类型转换在程序设计中都是不可避免的问题。当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要 去关注这方面的问题。但是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
>
|