分享
  1. 首页
  2. 文章

golang的类型转换的坑和分析

cangdu · · 11709 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

首先,我们来看一个例子

type Stringer interface {
 String() string
}
type String struct {
 data string
}
func (s *String) String() string {
 return data
}

上面是类型,然后

func GetString() *String {
 return nil
}
func CheckString(s Stringer) bool {
 return s == nil
}
func main() {
 println(CheckString(GetString()))
}

你们猜答案是什么?
当然,这么诡异的提问方式一看答案就是不合常理的false。
CheckString里面,s是不等于nil的。
如果你觉得不可思议,那么可以继续看下去了。

需要强调的是,本篇文章仅仅适用于golang。

类型转换

官方文档明确说明了怎么判断类型T是否可以转换成V,正常来说,T要转换成V必需显式声明。

func check64(v int64){}
func check(v int){}
a := 5
b := int64(10)
check64(int64(a))
check(int(b))
check64(a) // panic

不过,如果在满足Assignability的情况下,就可以在没有显式声明的情况下自动进行类型转换
其中,比较容易被忽略的是这两条:

  1. x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
  2. x is an untyped constant representable by a value of type T.
const a = 5
func check64(v int64){}
func checkSlice(v []int){}
check64(a) // -> check64(int64(a))
check64(1024) // -> check64(int64(1024))
checkSlice(nil) // -> checkSlice([]int(nil))

上面的代码是能够正确执行了, 需要注意的是,1024也是属于常量(这里说明了Integer literals是表示了一个integer constant).

分析

当了解之后,再看我刚开始给的例子

func GetString() *String {
 return nil
}
println(interface{}(GetString()))

这里扔出去的实际上是*String(nil), 如果print出来,应该是一个(0x****, 0x0)的值

需要ps的是,print打印一个interface时,第一个值为一个变量的动态类型的指针,第二个值为实际值的指针。这里为了得到动态类型,故意转换成了interface{}

然后,到CheckString,

func CheckString(s Stringer) bool {
 return s == nil
}

这里的nil会被转换成Stringer(nil), 用print大法打印出来,结果是(0x0, 0x0)

是不是感到很奇怪,print打印出来的第一个是类型的指针,为什么是0?首先,一个变量会有一个动态类型和静态类型,如果不是interface的话,静态类型和动态类型是一样的,当interface时,静态类型是interface的值,而动态类型确是他储存的值的实际类型。而print打印的是动态类型,由于这里他的值为0,动态类型也肯定为0.

当这时候执行CheckString(GetString())的时候,这时候s便是*String(nil), 而nil确是<nil>(nil)(空类型的空指针),进行equals判断的时候,根据spec, interface会在dynamic typedynamic value完全一样的时候才返回true。所以这里很明显是false。

如何不踩坑

虽然例子看起来很无厘头,但实际上这个比较容易踩坑的,特别是在想要作死的时候...

type SpecError struct {
 err Error
}
func NewSpecError(err error) *SpecError {
 if err == nil {
 return nil
 }
 return &SpecError{err}
}
func (se *SpecError) Error() string {
 return se.err.Error()
}
func GetObjByID(id string) (*Obj, error) {
 obj, err := xxxxx(id)
 return obj, NewSpecError(err)
}
func main(){
 obj, err := GetObjByID("123")
 if err != nil {
 panic(err)
 }
 。。。
}

好吧我承认只要不作死乖乖在GetObjByIDif err != nil不上就不会有这种事了...

思考

type RouterString func() string
type RouterBytes func() []byte
func AddRouter(router interface{}) {
 switch router := router.(type) {
 case RouterString:
 println("string")
 case RouterBytes:
 println("bytes")
 default:
 println("unknown types")
 }
}
func main() {
 AddRouter(func() string{
 println("hello?")
 })
}

这段代码哪里错了?怎么改?


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:cangdu

查看原文:golang的类型转换的坑和分析

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
11709 次点击
被以下专栏收入,发现更多相似内容
1 回复 | 直到 2018年05月03日 20:56:52
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏