golang 中类型断言类型转换两个概念很容易困惑,它们看上去提供了相同的功能(把变量从一个类型转到类型)。但是 golang 为什么会有两个功能相似的概念呢?那么在本文中,我们将了解类型断言类型转换本质的区别,并深入了解在 go 中使用它们会发生什么?

原文:https://www.sohamkamani.com/golang/type-assertions-vs-type-conversions/

首先让我们来看一下在 go 中如何使用它们:

类型断言
var greeting interface{} = "hello world"
greetingStr := greeting.(string)
类型转换
greeting := []byte("hello world")
greetingStr := string(greeting)
variable.(type)type(variable)

类型断言

greetinggreetinggreeting

注意:这里我们要考虑一个问题:在使用类型断言时是否一定要提前知道接口变量原始类型呢?如果我们不知道原始类型,或者接口变量不具备我们想目标类型会有什么结果呢?

我们来关注一下类型断言的另外一种用法

var greeting interface{} = "42"
greetingStr, ok := greeting.(string)
okbooltruefalsepanic

注意:上面这些表明了类型断言是在程序运行时执行的。

type switch
type switch
var greeting interface{} = 42

switch g := greeting.(type) {
  case string:
    fmt.Println("g is a string with length", len(g))
  case int:
    fmt.Println("g is an integer, whose value is", g)
  default:
    fmt.Println("I don't know what g is")
}

什么是断言

greeinginterface{}stringintgreeinggreeing

类型转换

在讲解类型转换前,首先让我们来理解一下什么是类型?golang 中类型定义了两个事情:

  • 数据结构:变量在底层以什么形式存储
  • 行为:变量具有哪些方法或者说是函数
stringintstructmapslice

基于基础类型我们还可以声明一个新的类型:

// myInt 是一个新的变量类型,它的基础类型是 int
type myInt int

// 函数 AddOne 工作在 myInt 上,和 int 没有任何关系
func (i myInt) AddOne() myInt { return i + 1}

func main() {
    var i myInt = 4
    fmt.Println(i.AddOne())
}
intmyIntmyInt
var i myInt = 4
originalInt := int(i)
imyintoriginalIntint

什么时候我们可以使用类型转换?

只有当两个类型的底层数据结构相同的时候,才可以使用类型转换,在做类型转换时不会检查类型的方法。让我们来看一个结构体的例子:

type person struct {
	name string
	age int
}

type child struct {
	name string
	age int
}

type pet {
  name string
}

func main() {
	bob := person{
		name: "bob",
		age: 15,
		}
  babyBob := child(bob)
  // "babyBob := pet(bob)" would result in a compilation error
	fmt.Println(bob, babyBob)
}
personchild
struct {
	name string
	age int
}

它们就可以相互进行类型转换

如果像上面这种声明多个具有相同的底层数据结构的结构体类型,可以有一个简洁的写法:

type person struct {
	name string
	age int
}

type child person

为什么叫做类型转换

正如上面提到的不同的类型,在其上的限制和方法是不同的,即使它们有着相同的底层数据结构。在我们使用 golang 的类型转换时我们改变的是这个变量的行为(因为不同的类型有不同的函数),而不是仅仅暴露这个变量的原始类型(后者是类型断言做的事)。另外,如果两个类型不能相互转换,golang 会在编译时报错,不像类型断言时在运行时报错。

总结

类型转换类型断言不仅仅是语法上的不同,通过这两个概念进一步强调了 golang 中接口类型非接口类型(具体类型)不同。接口类型没有底层数据结构,它仅仅是暴露了预先定义在具体类型中的方法。类型断言是获取隐藏在接口类型变量之下的具体类型,类型转换是改变我们操作变量底层数据方法