Go -- type 和断言 interface{}转换

摘要

类型转换在程序设计中都是不可避免的问题。当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要去关 注这方面的问题。但是golang中的类型匹配是很严格的,不同的类型之间通常需要手动转换,编译器不会代你去做这个事。我之所以说通常需要手动转换,是 因为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. 接口类型向普通类型转换需要类型断言。

普通类型向接口类型转换的例子随处可见,例如:


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>

时间: 2024-10-29 00:07:41

Go -- type 和断言 interface{}转换的相关文章

golang type 和断言 interface{}转换

摘要 类型转换在程序设计中都是不可避免的问题.当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要去关 注这方面的问题.但是golang中的类型匹配是很严格的,不同的类型之间通常需要手动转换,编译器不会代你去做这个事.我之所以说通常需要手动转换,是 因为interface类型作为一个特例,会有不同的处理方式. 类型转换在程序设计中都是不可避免的问题.当然有一些语言将这个过程给模糊了,大多数时候开发者并不需要 去关注这方面的问题.但是golang中的类型匹配是很严格的,不同的类型之间通常需要

【转】GO语言map类型interface{}转换踩坑小记

原文:https://www.az1314.cn/art/69 ------------------------------------------ mapA := make([string]interface{}) mapB := make([string]interface{}) mapA["name"] = "小文" mapA["age"]  = 25 mapB["mapA"] = mapA for k, v := ra

Go中interface嵌入和interface转换

// code_22_interface_field_interface project main.go package main import ( "fmt" ) type Humaner interface { SayHi() } type Personer interface { Humaner Sing(lyrics string) //lyrics 歌词 } type Student struct { name string score float64 } func (s *

TypeScript: type alias 与 interface

官方文档中有关于两者对比的信息,隐藏在 TypeScript Handbook 中,见 Interfaces vs. Type Aliases 部分. 但因为这一部分很久没更新了,所以其中描述的内容不一定全对. 比如, 区别点之一:Type Alias 不会创建新的类型,体现在错误信息上. One difference is, that interfaces create a new name that is used everywhere. Type aliases don't create

golang 接口interface{}、断言、switch type

第一大部分 interface{} 可以接受任何类型的对象值 获取interface{}队形的数据类型,可以使用断言,或者 switch type 来实现 // Assertion project main.go package main import ( "fmt" ) type Bag struct { Key string } type Bag2 struct { Key int } func main() { var b1 interface{} var b2 interfac

Using an Interface as a Type

When you define a new interface, you are defining a new reference data type. You can use interface names anywhere you can use any other data type name. If you define a reference variable whose type is an interface, any object you assign to itmust be

重学Golang系列(一): 深入理解 interface和reflect

前言 interface(即接口),是Go语言中一个重要的概念和知识点,而功能强大的reflect正是基于interface.本文即是对Go语言中的interface和reflect基础概念和用法的一次梳理,也算是我阶段学习的总结,以期温故而知新. interface(接口) 定义 在Go语言中,如果自定义类型(比如struct)实现了某个interface中的所有方法,那么就可以说这个类型实现了这个接口.接口可如下定义: type 接口名称 interface { method1(参数列表)

golang 类型断言的学习

在php中有一个 serialize() 函数 可以把数组序列化成字符串进行存储和传输 如果想反序列化这种字符串,在php中只需要一个简单的unserialize() 函数就可以完成了.但是在golang中可就没有这么容易了,非得费个九牛二虎之力,写上不少代码才行. 这时候只想感叹一下,php真的是世界上最好的语言啊! 我就在今天的开发中遇到了这么个问题,需要使用golang去解析php序列化的字符串,在github上找了个解析的包,但是发现解析之后的结果是个interface{}类型. 顿时我

Effective C++ -----条款19:设计class犹如设计type

Class的设计就是type的设计.在定义一个新type之前,请确定你已经考虑过本条款覆盖的所有讨论主题. 新type的对象应该如何被创建和销毁? 对象的初始化和对象的赋值该有什么样的区别? 新type的对象如果被passed by value(以值传递),意味着什么? 什么是新type的“合法值”? 你的新type需要配合某个继承图系(inheritance graph)吗? 你的新type需要什么样的转换? 什么样的操作符和函数对此新type而言是合理的? 什么样的标准函数应该驳回? 谁该取