Golang类型断言问题探讨
Golang类型断言问题探讨 我正在尝试理解一个类型断言的细节。以下是示例:
type StringThing string
var a interface{} = StringThing("test")
x, ok := a.(StringThing)
fmt.Println(x, ok) // "test true"
y, ok := a.(string)
fmt.Println(y, ok) // " false"
以及:
type ArrayThing []interface{}
var a interface{} = ArrayThing{"one",2}
x, ok := a.(ArrayThing)
fmt.Println(x, ok) // "[one 2] true"
y, ok := a.([]interface{})
fmt.Println(y, ok) // "[] false"
这些代码展示了问题所在。
我不太确定为什么在这些情况下,类型断言与底层类型定义不匹配。我并不是说这里的行为不正确,我只是想知道其中的原理。
我目前的想法是,这些是类型断言,而不是类型转换,并且特定的 type 声明创建了新的、独立的类型,而不是底层类型的宏。因此,每种情况下的第二个断言都失败了。
话虽如此,断言的要点似乎是编译器可以对值做出某种假设(例如,在每种情况下都可以遍历 interface{} 数组),并且在两种情况下,这个假设似乎都是有效的。
更多关于Golang类型断言问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
以下代码:
type StringThing = string
var a interface{} = StringThing("test")
x, ok := a.(StringThing)
fmt.Println(x, ok) // "test true"
y, ok := a.(string)
fmt.Println(y, ok) // "test true"
和
type ArrayThing = []interface{}
var a interface{} = ArrayThing{"one",2}
x, ok := a.(ArrayThing)
fmt.Println(x, ok) // "[one 2] true"
y, ok := a.([]interface{})
fmt.Println(y, ok) // "[one 2] true"
的运行结果与我最初的预期一致。
在类型声明中使用等号不会创建一个新的、独立的类型。
在类型声明中使用等号不会创建一个新的独立类型。
再次强调,Go编译器实现了语言规范。
类型声明将一个标识符(类型名称)绑定到一个类型。类型声明有两种形式:别名声明和类型定义。
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) . TypeSpec = AliasDecl | TypeDef .别名声明
别名声明将一个标识符绑定到给定的类型。
AliasDecl = identifier "=" Type .在该标识符的作用域内,它作为该类型的别名。
你将你的类型声明从类型定义
type StringThing string
改为了别名声明
type StringThing = string
对于别名声明,StringThing 和 string 表示相同的类型。
类型别名声明有助于在大规模重构期间进行渐进式代码修复:提案:类型别名。
mAdkins23:
我当前的想法
Go 编译器实现的是语言规范,而不是你的想法。
类型定义
类型定义会创建一个新的、独特的类型,该类型具有与给定类型相同的基础类型和操作,并将一个标识符绑定到它。
TypeDef = identifier Type .这个新类型被称为已定义类型。它不同于任何其他类型,包括创建它的原始类型。
对于一个接口类型的表达式 x 和一个类型 T,主表达式
x.(T)断言 x 不是 nil 并且存储在 x 中的值是 T 类型。符号 x.(T) 被称为类型断言。
更精确地说,如果 T 不是接口类型,x.(T) 断言 x 的动态类型与类型 T 完全相同。
两个类型要么相同,要么不同。
一个已定义类型总是不同于任何其他类型。
StringThing 和 string 是不同的、独立的类型。它们不是同一的。
在Go语言中,类型断言确实不是类型转换,你的理解基本正确。类型断言用于检查接口值是否持有特定的具体类型,而类型转换则是在具有相同底层类型的类型之间进行转换。以下是详细解释和示例:
1. 类型断言的工作原理
类型断言 x.(T) 检查接口值 x 是否持有类型 T。如果 T 是具体类型,断言会检查 x 的动态类型是否与 T 完全相同。如果 T 是接口类型,断言会检查 x 的动态类型是否实现了 T。
2. 为什么第二个断言失败
在Go中,使用 type 定义的新类型(如 StringThing 或 ArrayThing)是独立的类型,即使它们共享相同的底层类型。类型断言要求类型完全匹配,因此:
StringThing和string是不同的类型。ArrayThing和[]interface{}是不同的类型。
3. 示例代码分析
示例1:StringThing 和 string
type StringThing string
var a interface{} = StringThing("test")
x, ok := a.(StringThing) // 成功:a 的动态类型是 StringThing
fmt.Println(x, ok) // 输出: test true
y, ok := a.(string) // 失败:a 的动态类型是 StringThing,不是 string
fmt.Println(y, ok) // 输出: false
示例2:ArrayThing 和 []interface{}
type ArrayThing []interface{}
var a interface{} = ArrayThing{"one", 2}
x, ok := a.(ArrayThing) // 成功:a 的动态类型是 ArrayThing
fmt.Println(x, ok) // 输出: [one 2] true
y, ok := a.([]interface{}) // 失败:a 的动态类型是 ArrayThing,不是 []interface{}
fmt.Println(y, ok) // 输出: [] false
4. 如何实现类型转换
如果需要将 StringThing 转换为 string,或 ArrayThing 转换为 []interface{},必须使用显式类型转换,因为它们是不同的类型。但注意:类型转换要求两者具有相同的底层类型。
// StringThing 转换为 string
type StringThing string
var st StringThing = "test"
s := string(st) // 类型转换
fmt.Println(s) // 输出: test
// ArrayThing 转换为 []interface{}
type ArrayThing []interface{}
at := ArrayThing{"one", 2}
slice := []interface{}(at) // 类型转换
fmt.Println(slice) // 输出: [one 2]
5. 接口类型断言的特殊情况
如果类型断言的目标是接口类型,Go会检查动态类型是否实现了该接口。例如:
type Reader interface {
Read(p []byte) (n int, err error)
}
type MyReader struct{}
func (m MyReader) Read(p []byte) (n int, err error) {
return len(p), nil
}
var a interface{} = MyReader{}
r, ok := a.(Reader) // 成功:MyReader 实现了 Reader 接口
fmt.Println(r, ok) // 输出: {} true
总结
类型断言严格检查接口值的动态类型是否与目标类型完全相同。使用 type 定义的新类型是独立类型,即使底层类型相同,类型断言也不会将其视为相同类型。如果需要转换,必须使用显式类型转换(前提是底层类型相同)。


