Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

26|方法:如何用类型嵌入模拟实现“继承”?

结构体类型嵌入的多个接口类型的方法集合存在交集时,可能会导致编译错误
结构体类型的方法集合包含嵌入的结构体类型的方法集合
结构体类型的方法集合包含嵌入的接口类型的方法集合
结构体类型的类型嵌入是一种组合中的代理(delegate)模式
结构体类型嵌入接口类型可以简化单元测试的编写
嵌入字段的类型的可见性与嵌入字段的可见性是一致的
接口类型的类型嵌入的语义就是新接口类型将嵌入的接口类型的方法集合,并入到自己的方法集合中
接口类型声明了由一个方法集合代表的接口
Go语言支持两种类型嵌入,分别是接口类型的类型嵌入和结构体类型的类型嵌入
类型嵌入指的就是在一个类型的定义中嵌入了其他类型
带有类型嵌入的结构体S1与不带类型嵌入的结构体S2是否是等价的,如不等价,区别在哪里?
类型别名定义的新类型与原类型拥有完全相同的方法集合
基于非接口类型的defined类型创建的非接口类型不会继承原类型的方法集合
基于接口类型创建的defined的接口类型的方法集合与原接口类型的方法集合是一致的
结构体类型的类型嵌入
接口类型的类型嵌入
思考题
类型嵌入与方法集合
什么是类型嵌入
Go类型嵌入

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
在前面的两节课中,我们学习了 Go 方法的声明、本质,以及 receiver 类型选择的三个原则。可以说,学完这些内容,我们就基本上解决了独立的自定义类型的方法的设计问题。
什么是独立的自定义类型呢?就是这个类型的所有方法都是自己显式实现的。我们举个例子,自定义类型 T 有两个方法 M1 和 M2,如果 T 是一个独立的自定义类型,那我们在声明类型 T 的 Go 包源码文件中一定可以找到其所有方法的实现代码,比如:
func (T) M1() {...}
func (T) M2() {...}
这里你一定会问:难道还有某种自定义类型的方法不是自己显式实现的吗?当然有!这就是我们这讲中要重点讲解的内容:如何让某个自定义类型“继承”其他类型的方法实现
这里你肯定又会提出质疑:老师,你不是说过 Go 不支持经典的面向对象编程范式吗?怎么还会有继承这一说法呢?没错!Go 语言从设计伊始,就决定不支持经典面向对象的编程范式与语法元素,所以我们这里只是借用了“继承”这个词汇而已,说是“继承”,实则依旧是一种组合的思想。
而这种“继承”,我们是通过 Go 语言的类型嵌入(Type Embedding)来实现的。所以这一节课,我们就来学习一下这种语法,看看通过这种语法,我们如何实现对嵌入类型的方法的“继承”,同时也搞清楚这种方式对新定义的类型的方法集合的影响。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go语言中的类型嵌入是一项强大的特性,可以模拟实现“继承”,通过嵌入其他类型的方法来扩展自定义类型的功能。本文首先介绍了接口类型的类型嵌入,通过案例展示了如何在一个接口类型中嵌入另一个接口类型,实现接口的组合。随后,文章详细介绍了结构体类型的类型嵌入,解释了这种方式对新定义的类型的方法集合的影响。文章还提到了Go语言中的组合设计哲学,强调了类型嵌入的重要性和灵活性。通过实例和讲解清晰地展示了类型嵌入的原理和方法集合的变化,为读者提供了深入理解和应用该特性的基础。文章通过对接口类型和结构体类型的类型嵌入进行详细讲解,展示了Go语言中类型嵌入的灵活性和强大功能。读者可以从中快速了解并掌握类型嵌入的使用方法和技术特点,为日后的Go语言开发提供了有力的参考和指导。 在文章中,我们学习了类型嵌入相关的知识,包括接口类型和结构体类型的类型嵌入。类型嵌入对类型方法集合的影响是我们日常进行方法设计时必须要考虑到的重要因素。类型嵌入分为两种,一种是接口类型的类型嵌入,另一种是结构体类型的类型嵌入。通过实例和讲解,我们了解到类型嵌入实际上是一种组合,更具体地说是组合思想下代理模式的运用。最后,我们还总结了基于非接口类型的defined类型创建的新defined类型不会继承原类型的方法集合,而通过类型别名定义的新类型则和原类型拥有相同的方法集合。 通过本文的学习,读者可以快速了解并掌握类型嵌入的使用方法和技术特点,为日后的Go语言开发提供了有力的参考和指导。文章内容丰富,深入浅出,适合对Go类型嵌入感兴趣的读者。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(30)

  • 最新
  • 精选
  • S2 不是嵌入的,相当的正常书写的字段,所有没有 对应接口的方法集合

    作者回复: ✅

    2021-12-14
    3
    57
  • liaomars
    带有类型嵌入的结构体 S1 与不带类型嵌入的结构体 S2 是否是等价的,如不等价,区别在哪里? 这两个S1与S2是不等价的,区别是:S1结构体能调用代理嵌入类型的所有方法,S2结构体是没有代理嵌入类型方法。

    作者回复: 正确✅

    2021-12-13
    12
  • 13693113483
    可不可以这样认为,直接 T1 *t2 I 我们可以理解为是继承,里面所有方法可以调用,然而: T1 T1 t2 *t2 I I 这种是组合,如果想调用里面的方法要用 S2.I.M1()

    作者回复: 两种本质上都是组合。第一种可以理解为一种“语法糖”,无需再在外层结构上重新定义方法并代理给内部字段。第二种从能力上讲和第一种没有本质区别,只是需要开发者自己在外层封一层方法,然后调用字段对应的方法。

    2022-06-23
    6
  • 菠萝吹雪—Code
    这篇真干,赞,需要多看几遍

    作者回复: 👍

    2022-08-19归属地:北京
    3
  • Aeins
    结构体类型,两种不同方式嵌套接口类型。 由于接口类型嵌套允许重名方法,I 接口有三个方法。SI 嵌套 I,SI 也有三个方法 结构体嵌套不允许重名,M 方法,被自动隐藏了?,SI12嵌套 I1, I2, SI12 只有两个方法 package main import ( "fmt" "reflect" ) type I1 interface { M() M1() } type I2 interface { M() M2() } type I interface { I1 I2 } type SI struct { I } type SI12 struct { I1 I2 } func main() { var si SI var si12 SI12 DumpMethodSet(si) DumpMethodSet(si12) } func DumpMethodSet(i interface{}) { dynTyp := reflect.TypeOf(i) if dynTyp == nil { fmt.Printf("there is no dynamic type\n") return } n := dynTyp.NumMethod() if n == 0 { fmt.Printf("%s's method set is empty!\n", dynTyp) return } fmt.Printf("%s's method set:\n", dynTyp) for j := 0; j < n; j++ { fmt.Println("-", dynTyp.Method(j).Name) } fmt.Printf("\n") } ============ main.SI's method set: - M - M1 - M2 main.SI12's method set: - M1 - M2

    作者回复: 问题非常好!👍 我们知道:一个类型的方法集合中的方法都可以被这个类型实例所调用。反过来说,只有能被类型实例直接调用的方法才能进入其方法集合。 在你的例子中,SI12嵌入了I1和I2,这就造成了I1和I2的方法集合的交集M在SI12中存在了歧义。当使用SI12的实例调用M时,比如: var s SI12 s.M() // go编译器会报错!不确定究竟是调用s.I1.M还是s.I2.M 于是M因这种未决的歧义性而不能被列入SI12的方法集合中。

    2022-05-30
    2
    3
  • Geek_99b47c
    S1's method set total 1, detail method : -- M1 S2's method set is empty 这是为啥啊

    作者回复: 建议再重新学习一下25讲与26讲。只有自己理解后,知识才是自己的。

    2021-12-13
    2
    3
  • 不求闻达
    不等价,S2 不是嵌入的是defined 类型。defined 类型创建的新 defined 类型不会继承原类型的方法集合

    作者回复: 正确✅👍

    2023-04-06归属地:上海
    2
  • Fan()
    类型 *T 的方法集合 = *T1 的方法集合 + *T2 的方法集合 对这个不太理解... 有点懵逼. 这个原理能细讲一下吗?

    作者回复: 这个没有什么原理可讲,Go语言就是这么规定的:)。

    2022-04-03
    2
  • Calvin
    不等价,dumpMethodSet 结果就不一样: S1's method set: - M1 *S1's method set: - M1 S2's method set is empty! *S2's method set is empty!

    作者回复: 👍

    2021-12-20
    2
  • lesserror
    Tony bai 老师,我看打印出的方法集合中都有类似这样的输出:*main.T's 。后面会讲解一下这种语法输出吗? 对于课后问题,我觉得用一下老师的 dumpMethodSet 函数 就能很容易看出区别了。 package main import ( "fmt" "reflect" ) type T1 int type t2 struct{ n int m int } type I interface { M1() } type S1 struct { T1 *t2 I a int b string } type S2 struct { T1 T1 t2 *t2 I I a int b string } func main() { var s1 S1 var ps1 *S1 var s2 S2 var ps2 *S2 dumpMethodSet(s1) dumpMethodSet(ps1) dumpMethodSet(s2) dumpMethodSet(ps2) } func dumpMethodSet(i interface{}) { dynTyp := reflect.TypeOf(i) if dynTyp == nil { fmt.Printf("there is no dynamic type\n") return } n := dynTyp.NumMethod() if n == 0 { fmt.Printf("%s's method set is empty!\n", dynTyp) return } fmt.Printf("%s's method set:\n", dynTyp) for j := 0; j < n; j++ { fmt.Println("-", dynTyp.Method(j).Name) } fmt.Printf("\n") }

    作者回复: *main.T其实也是一种指针类型,还是等我的加餐吧:(。

    2021-12-14
    2
    2
收起评论
显示
设置
留言
30
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部