golang学习笔记——类型断言
interface{} 好比 java 中的 Object,java 中的所有类都实现了Object。从测试代码中可以看出,结构体可以断言原本的类型,也可以断言是否继承,实际上这两个分支都是可以进去的。,程序会终止,显然并不能满足一些业务的需求,比如记录错误日志,或走其他分支等等,所以。的参数,就代表了它可以传递任何类型的变量。,再加上**switch **判断类型 不可以使用。多说一句,出于代码
文章目录
类型断言
Go语言的类型断言与类型转换在形式上都相差一小点,之前我还一直以为是类型转换。没想到这是错误的。
在 Golang 中,类型转换和类型断言是两种不同的操作。类型转换是将不同类型的变量进行转换,需要开发人员进行显式的类型转换操作。类型断言是将接口类型转换为具体的类型,或者将具体类型转换为接口。类型断言的成功与失败取决于接口值的动态类型是否与尝试断言的类型相匹配。
Go 语言类型断言
GoLang中的 interface{} 即 any 可以代表所有类型,包括基本类型string、int、int64,以及自定义的 struct 类型。
interface{} 好比 java 中的 Object,java 中的所有类都实现了Object。
下面这个函数接收一个 interface{} 的参数,就代表了它可以传递任何类型的变量。如果在代码中强转 a 为 string,如以下的代码:
func funcName(a interface{}) string {
return string(a)
}
此时编译器将会返回:
cannot convert a (type interface{}) to type string: need type assertion
编译器提示我们用类型断言(type assertion)约束变量。接下来介绍几种使用类型断言的方法。
直接断言使用
直接断言使用,返回对应类型的值
- 如果断言成功,则 变量b == 变量a
- 如果断言失败,则 panic
语法如下:
变量b :=变量a.(类型)
举例
如果 断言为 string,则输出正常name type = string, name = tom
如果断言为 非 string,则 panic
package main
import "fmt"
import "reflect"
func testTypeAssertion1() {
var name any = "tom"
nameType := reflect.TypeOf(name)
fmt.Printf("name type = %v, name = %v\n", nameType, name.(string))
fmt.Printf("name type = %v, name = %v\n", nameType, name.(int))
}
func main() {
testTypeAssertion1()
}
输出
name type = string, name = tom
panic: interface conversion: interface {} is string, not int
第一个fmt正常输出,第二个fmt发生panic
断言判断并返回是否成功,可配合if
从一些业务角度来讲,如果断言不成功直接 panic,程序会终止,显然并不能满足一些业务的需求,比如记录错误日志,或走其他分支等等,所以 GoLang 也提供了另一个参数,返回断言是否成功
- 如果断言成功,返回对应类型的值,即 变量b == 变量a,ok 为 true
- 如果断言失败,变量b为对应 断言类型的 默认值,ok 为 false
语法如下:
变量b, ok = 变量a.(类型)
举例
func testTypeAssertion2() {
var name any = "tom"
nameStringData, ok1 := name.(string)
fmt.Printf("name type = %v, name = %v, assert success ? %v, ok type = %T\n", reflect.TypeOf(nameStringData), nameStringData, ok1, ok1)
nameIntData, ok2 := name.(int)
fmt.Printf("name type = %v, name = %v, assert success ? %v, ok type = %T\n", reflect.TypeOf(nameIntData), nameIntData, ok2, ok2)
}
输出结果,不出意外,没有 panic,可以看出,第二个参数 ok 返回了断言是否成功, bool类型
name type = string, name = tom, assert success ? true, ok type = bool
name type = int, name = 0, assert success ? false, ok type = bool
也可以看出转 int 未成功后的 nameIntData 为 int 类型,默认值是 0
断言成功或失败,结合 if 执行不同的操作
if ok1 {
name = "Mike"
fmt.Printf("ok1 true, change name to %v\n", name)
} else {
name = "mike"
fmt.Printf("ok1 false, change name to %v\n", name)
}
if ok2 {
name = "sara"
fmt.Printf("ok2 true, change name to %v\n", name)
} else {
name = ""
fmt.Printf("ok2 false, change name to %v\n", name)
}
输出结果
name type = string, name = tom, assert success ? true, ok type = bool
name type = int, name = 0, assert success ? false, ok type = bool
ok1 true, change name to Mike
ok2 false, change name to
将断言语句与 if 结合起来使用
将断言语句与 if 结合 ,写在一行
if aData, ok := a.(T); ok {
fmt.Println(aData, ok)
}
测试:
func testTypeAssertion3() {
var name any = "tom"
if nameStringData, ok1 := name.(string); ok1 {
nameStringData = "Mike"
fmt.Printf("ok1 true, change name to %v\n", nameStringData)
} else {
nameStringData = "mike"
fmt.Printf("ok1 false, change name to %v\n", nameStringData)
}
if nameIntData, ok2 := name.(int); ok2 {
nameIntData = 10086
fmt.Printf("ok2 true, change name to %v\n", nameIntData)
} else {
nameIntData = 10010
fmt.Printf("ok2 false, change name to %v\n", nameIntData)
}
}
输出结果
ok1 true, change name to Mike
ok2 false, change name to 10010
多说一句,出于代码可读性角度来讲,编码时可以使用类型语句减少代码复杂度,少用 if else,提前退出逻辑。
switch 断言类型
如果一次性断言多种类型,会写很多的 if else,也可以用 switch 减少代码复杂度,用 变量.(type) 作为判断条件,格式如下:
switch variable := variable.(type){
default:
fmt.Printf("unexpected type %T", variable)
case string:
fmt.Printf("type is %T, variable = %v", variable, variable)
case int:
fmt.Printf("type is %T, variable = %v", variable, variable)
case bool:
fmt.Printf("type is %T, variable = %v", variable, variable)
case float32:
fmt.Printf("type is %T, variable = %v", variable, variable)
}
测试
func testTypeAssertion4() {
var a any = 2
switch a := a.(type) {
default:
fmt.Printf("unexpected type %T", a)
case string:
fmt.Printf("type is %T, value = %v", a, a)
case int:
fmt.Printf("type is %T, value = %v", a, a)
case bool:
fmt.Printf("type is %T, value = %v", a, a)
case float32:
fmt.Printf("type is %T, value = %v", a, a)
}
}
输出
type is int, value = 2
结构体类型断言
首先定义接口和结构体
type Color interface {
getColor() Color
setColor(value int64, content string)
}
type Red struct {
Value int64 `json:"value"`
Content string `json:"content"`
}
type Yellow struct {
Value int64 `json:"value"`
Content string `json:"content"`
}
然后让 Red 实现 Color, Yellow不实现Color
func (y Yellow) getColor2() *Yellow {
return &Yellow{
Value: 0xF6FF33,
Content: "黄色",
}
}
func (y Yellow) setColor(value int64, content string) {
y.Value = value
y.Content = content
}
func (r Red) getColor() Color {
return &Red{
Value: 0xFF5733,
Content: "红色",
}
}
func (r Red) setColor(value int64, content string) {
r.Value = value
r.Content = content
}
测试:
func testTypeAssertion5() {
var red interface{} = &Red{}
var yellow interface{} = &Yellow{}
if color, ok := red.(Color); ok {
fmt.Printf("red ok = %v, color = %v\n", ok, color.getColor())
} else {
fmt.Printf("red ok = %v, color = %v\n", ok, red)
}
if color, ok := yellow.(Color); ok {
fmt.Printf("yellow ok = %v, color = %v\n", ok, color.getColor())
} else {
fmt.Printf("yellow ok = %v, color = %v\n", ok, yellow)
}
switch red := red.(type) {
default:
fmt.Printf("default no color, red = %v\n", red)
case Red:
fmt.Printf("color = Red, %v\n", red)
case *Red:
fmt.Printf("color = *Red, %v\n", red)
case Color:
fmt.Printf("color = Color, %v\n", red)
}
}
输出
red ok = true, color = &{16734003 红色}
yellow ok = false, color = &{0 }
color = *Red, &{0 }
从测试代码中可以看出,结构体可以断言原本的类型,也可以断言是否继承,实际上这两个分支都是可以进去的
case *Red:
fmt.Printf("color = *Red, %v\n", red)
case Color:
fmt.Printf("color = Color, %v\n", red)
因为先匹配到了 *Red,再加上**switch **判断类型 不可以使用 fallthrough,所以如果切换顺序,会匹配到 Color
case Color:
fmt.Printf("color = Color, %v\n", red)
case *Red:
fmt.Printf("color = *Red, %v\n", red)
输出
red ok = true, color = &{16734003 红色}
yellow ok = false, color = &{0 }
color = Color, &{0 }
以下三种方式都可以在编译前看到结构体是否实现了接口
var _ Color = &Red{}
var _ Color = Red{}
var _ Color =(*Red)(nil)
var _ Color = &Yellow{}
对 Red 使用可,对 Yellow 使用,编译器会提示错误
完整代码
package main
import "fmt"
import "reflect"
func testTypeAssertion1() {
var name any = "tom"
nameType := reflect.TypeOf(name)
fmt.Printf("name type = %v, name = %v\n", nameType, name.(string))
fmt.Printf("name type = %v, name = %v\n", nameType, name.(int))
}
func testTypeAssertion2() {
var name any = "tom"
nameStringData, ok1 := name.(string)
fmt.Printf("name type = %v, name = %v, assert success ? %v, ok type = %T\n",
reflect.TypeOf(nameStringData), nameStringData, ok1, ok1)
nameIntData, ok2 := name.(int)
fmt.Printf("name type = %v, name = %v, assert success ? %v, ok type = %T\n",
reflect.TypeOf(nameIntData), nameIntData, ok2, ok2)
}
type Color interface {
getColor() Color
setColor(value int64, content string)
}
type Red struct {
Value int64 `json:"value"`
Content string `json:"content"`
}
type Yellow struct {
Value int64 `json:"value"`
Content string `json:"content"`
}
func (y Yellow) getColor2() *Yellow {
return &Yellow{
Value: 0xF6FF33,
Content: "黄色",
}
}
func (y Yellow) setColor(value int64, content string) {
y.Value = value
y.Content = content
}
func (r Red) getColor() Color {
return &Red{
Value: 0xFF5733,
Content: "红色",
}
}
func (r Red) setColor(value int64, content string) {
r.Value = value
r.Content = content
}
func testTypeAssertion5() {
var red interface{} = &Red{}
var yellow interface{} = &Yellow{}
if color, ok := red.(Color); ok {
fmt.Printf("red ok = %v, color = %v\n", ok, color.getColor())
} else {
fmt.Printf("red ok = %v, color = %v\n", ok, red)
}
if color, ok := yellow.(Color); ok {
fmt.Printf("yellow ok = %v, color = %v\n", ok, color.getColor())
} else {
fmt.Printf("yellow ok = %v, color = %v\n", ok, yellow)
}
switch red := red.(type) {
default:
fmt.Printf("default no color, red = %v\n", red)
case Red:
fmt.Printf("color = Red, %v\n", red)
case *Red:
fmt.Printf("color = *Red, %v\n", red)
case Color:
fmt.Printf("color = Color, %v\n", red)
}
}
func main() {
//实现了接口,没有错误提示
//var _ Color = &Red{}
//var _ Color = Red{}
//var _ Color = (*Red)(nil)
//没有实现接口,错误提示
//var _ Color = &Yellow{}
testTypeAssertion5()
}
参考资料
GoLang类型转换
GoLang类型断言使用汇总
Go语言的类型断言(Type Assertion)和类型转换(Type Conversion)有什么区别
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)