V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
gramyang
V2EX  ›  Go 编程语言

关于 go 接口和 go 面向对象问题的一点遐(扯)想(蛋)

  •  
  •   gramyang · 2019-07-23 08:40:27 +08:00 · 3679 次点击
    这是一个创建于 1948 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题的起因是看到一种很奇怪的写法: 结构体 A 实现了接口 B,其中一个接口 B 嵌套了接口 C、D,另外有一个方法 Fun(),B 的引用是 b。 b.(interface{ Fun() }).Fun() 这里调用的是结构体 A 的方法 Fun()。

    这里的问题就来了,这种写法比较奇怪,但是好歹算是正确的。如何保证它的正确性呢? 放在 Java 里肯定不行,因为 Java 是有类继承的,类的类型可以在子类父类间转换。这种写法只能在 Fun()只有一个实现的情况下才能保证正确性,然而 golang 其实也是有重写这个概念的。

    查了一下发现,类型断言 v := varI.(T),其中 varI 只能是接口。或许是因为这个机制从而保证了上述写法的正确性。

    请指正!

    第 1 条附言  ·  2019-07-23 09:49:24 +08:00
    后来想了想,其实 go 接口的这个特性和 go 的重写没关系。。。。
    第 2 条附言  ·  2019-07-23 10:45:33 +08:00
    最新发现,b.(interface{ Fun() }).Fun()的应用:

    有结构体 A,嵌套结构体 B、C。结构体 A 可以直接调用结构体 B 和结构体 C 的方法。但是结构体 A 实现的接口的引用是不能调用结构体 B 和结构体 C 的方法的。

    b.(interface{ Fun() }).Fun()可以解决这个问题。
    20 条回复    2019-07-23 13:56:35 +08:00
    lhx2008
        1
    lhx2008  
       2019-07-23 08:45:30 +08:00 via Android
    和 java 的没啥关系,只要是名字参数都符合就可以转过去了,也没有啥基类派生类。。
    wweir
        2
    wweir  
       2019-07-23 08:53:46 +08:00
    这种写法只能人为保证正确性,机制上只提供了一个 ok 返回值供判断
    shingle
        3
    shingle  
       2019-07-23 08:58:38 +08:00 via Android
    可以编译期判断 var _ A = new(B)
    Mohanson
        4
    Mohanson  
       2019-07-23 09:01:10 +08:00 via Android
    不知道为什么上来就又要面向对象…何必呢
    mcfog
        5
    mcfog  
       2019-07-23 09:21:52 +08:00 via Android
    其实就是懒得写一个 Fun 的接口了而已,为此丢了编译期检查,又不愿意判 ok 引入 panic 的风险 :doge:
    gramyang
        6
    gramyang  
    OP
       2019-07-23 09:29:31 +08:00
    @mcfog 不是,你把接口改成 B 也行。
    我觉得这么写可能是因为阅读方便,原来的 B 接口有 N 多个方法,而你只用 B 接口中的几个方法,把他们组成一个接口,这样阅读性更好些。
    不过我还是觉得这种写法不太自然
    janxin
        7
    janxin  
       2019-07-23 09:36:02 +08:00
    我理解能力差,没太看懂...能具体解释一下吗
    gramyang
        8
    gramyang  
    OP
       2019-07-23 09:48:01 +08:00
    @janxin go 的隐式实现接口的特性可以让它凭空实现一个含结构体方法的接口(所谓无中生妈),这种写法其实没卵用,或者说我现在还没有发现它有什么卵用。
    go 用组合代替继承,可以实现重写。
    后来想了想,其实重写和接口没关系。。。
    Hyvi
        9
    Hyvi  
       2019-07-23 10:02:24 +08:00 via Android
    什么叫接口嵌套接口
    rrfeng
        10
    rrfeng  
       2019-07-23 10:28:51 +08:00
    A 实现了接口 B,A 有一个实例叫 b
    b.(B).Func() 么这不就是???

    没看懂楼主想说什么……
    本来 b 就可以直接 b.Func() 啊???
    gramyang
        11
    gramyang  
    OP
       2019-07-23 10:32:01 +08:00
    @rrfeng 关键是这个:
    b.(interface{ Fun() }).Fun()
    gamexg
        12
    gamexg  
       2019-07-23 10:32:15 +08:00
    go 里面也不行,
    编译时无法保证 b 实现了 Fun() ,运行时如果 b 未实现 Fun() 会 panic。

    最好这样处理下:

    ```
    _fun,ok:=b.(interface{ Fun() })
    if !ok{
    return fmt.ErrorF("")
    }
    _fun.Fun()
    ```
    mcfog
        13
    mcfog  
       2019-07-23 10:46:38 +08:00 via Android
    @rrfeng Fun 是 A 结构体的,类型为 B 的变量 b 即使实体是 A 也没法直接调到,所以才有那个匿名接口的类型断言
    gramyang
        14
    gramyang  
    OP
       2019-07-23 10:47:10 +08:00
    @gamexg emmm,一般用 goland 写,goland 会自动检测这个方法有没有被实现。。。
    你要是用 vscode 什么的确实会有这种问题。
    rrfeng
        15
    rrfeng  
       2019-07-23 11:44:47 +08:00
    @mcfog
    但是 B 没实现 Func 的话,断言会失败啊。
    所以 B 实现了 Func,那么断言之后调用 Func 还是 B 的 Func 吧?
    tairan2006
        16
    tairan2006  
       2019-07-23 11:46:26 +08:00
    没法保证,你要人肉保证

    void*在 c 里面可以随便转成函数指针
    rrfeng
        17
    rrfeng  
       2019-07-23 11:53:14 +08:00   ❤️ 1
    看了最新的补充才看懂是在说什么……你们怎么就一下子明白了楼主的意思的???
    reus
        18
    reus  
       2019-07-23 13:09:02 +08:00
    语言表达能力有待提高……

    “结构体 A 实现了接口 B ”,这句 ok
    “其中一个接口 B 嵌套了接口 C、D ”,接口 B 就只有一个,“其中一个”似乎多余
    “另外有一个方法 Fun()”,指代不明,究竟是 A 类型有 Fun 方法,还是 B 接口有 Fun 方法?
    “ B 的引用是 b ”,意义不明,B 是一个接口类型,接口类型的引用是啥?是指“有一个变量叫 b,类型是 B ”?

    第一段就不知所云,后面两段也不知道在说什么。

    后面那个“最新发现”,怎么忽然就变成结构体的嵌套了。后面这不就是最基本的类型断言么,感觉就是学得太少想得太多而已。
    GeruzoniAnsasu
        19
    GeruzoniAnsasu  
       2019-07-23 13:40:47 +08:00
    感觉就是某种 down cast
    那么为什么不 b.(A)呢?
    zzlettle
        20
    zzlettle  
       2019-07-23 13:56:35 +08:00
    b.(interface{ Fun() }).Fun()

    本人学习 GOLANG 不久,这个用法不太明白
    这行代码想要解决什么问题了?

    上面有人写的这段代码,我到能看懂
    ```
    _fun,ok:=b.(interface{ Fun() })
    if !ok{
    return fmt.ErrorF("")
    }
    _fun.Fun()
    ```
    这个是在判断,b 是否实现了含有 fun()的一个接口
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1054 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:06 · PVG 03:06 · LAX 11:06 · JFK 14:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.