golang中的匿名组合

确切地说,Go语言也提供了继承,但是采用了组合的文法,所以我们将其称为匿名组合:

type Base struct {

Name string

}

func (base *Base) Foo() {...}

func (base *Base) Bar() {...}

type Foo struct {

Base

...

}

func (foo *Foo) Bar() {

foo.Base.Bar()

...

}

以上代码定义了一个Base类(实现了Foo()和Bar()两个成员方法),然后定义了一个Foo类,该类从Base类“继承”并改写了Bar()方法(该方法实现时先调用了基类的Bar()方法)。

在“派生类”Foo没有写“基类”Base的成员方法时,相应的方法就被“继承”,例如在上面的例子中,调用foo.Foo()和调用foo.Base.Foo()效果一致。

与其他语言不同,Go语言很清晰地告诉你的内存布局是怎样的。此外,在Go语言中你还可以随心所欲地修改内存布局,如:

type Foo struct {

...//其他成员

Base

}

这段代码从语义上来说,和上面的例子并无不同,但内存布局发生了改变。“基类”Base的数据放在了“派生类”Foo的最后。

另外,在Go语言中,你还可以以指针方式从一类类型“派生”:

type Foo struct {

*Base

...

}

这段Go代码仍然有“派生”的效果,只是Foo创建实例的时候,需要外部提供一个Base类实例的指针。

在C++语言中其实也有类似的功能,那就是虚基类,但是它非常让人难以理解,一般C++的开发者都会遗忘这个特性。相比之下,Go语言以一种非常容易理解的方式提供了一些原本期望用虚基类才能解决的设计难题。

在Go语言官方网站提供的Effective Go中曾提到匿名组合的一个小价值,值得在这里再提一下。首先我们可以定义如下的类型,它匿名组合了一个log.logger指针:

type Job struct {

Command string

*log.Logger

}

在合适的赋值后,我们在Job类型的所有成员方法中可以很舒适地借用所有log.Logger提供的方法。比如如下的写法:

func (job *Job) Start() {

job.Log("starting now...")

...//做一些事情

job.Log("started.")

}

对于Job的实现者来说,他甚至根本就不用意识到log.logger类型的存在,这就是匿名组合的魅力所在。在实际工作中,只有合理利用才能最大发挥这个功能的价值。

需要注意的是,不管是非匿名的类型组合还是匿名组合,被组合的类型所包含的方法虽然都升级了外部这个组合类型的方法,但其实它们被组合的方法调用时接收者并没有改变。比如上面这个Job例子,即使组合后调用的方式变成了job.Log(...),但Log函数的接收者仍然是log.Logger指针,因此在Log中不可能访问到job的其他成员方法和变量。

这其实也很容易理解,毕竟被组合的类型并不知道自己会被什么类型组合,当然就没法在实现方法时去使用那个未知的“组合者”的功能了。

另外,我们必须关注一下接口组合中的名字冲突问题,比如如下的组合:

type X struct {

Name string

}

type Y struct {

X

Name string

}

组合的类型和被组合的类型都包含一个Name成员,会不会有问题呢?答案是否定的。所有的Y类型的Name成员的访问都只会访问到最外层的那个Name变量,X.Name变量相当于被隐藏起来了。

那么下面这样的场景呢:

type Logger struct {

level int

}

type Y struct {

*Logger

Name string

*log.Logger

}

显然这里会有问题。因为之前已经提到过,匿名组合类型相当于以其类型名称(去掉包名部分)作为成员变量的名字。按此规则,Y类型中就相当于存在两个名为Logger的成员,虽然类型不同。因此,我们预期会收到编译错误。

有意思的是,这个编译错误并不是一定会发生的。假如这两个Logger在定义后再也没有被用过,那么编译器将直接忽略掉这个冲突问题,直至开发者开始使用其中的某个Logger。

时间: 2024-10-26 14:38:21

golang中的匿名组合的相关文章

[golang note] 匿名组合

匿名组合 golang也提供了继承机制,但采用组合的文法,因此称为匿名组合.与其他语言不同, golang很清晰地展示出类的内存布局是怎样的. • 非指针方式组合 ?  基本语法 // 基类 type Base struct { // 成员变量 } func (b *Base) 函数名(参数列表) (返回值列表) { // 函数体 } // 派生类 type Derived struct { Base // 成员变量 } func (b *Derived) 函数名(参数列表) (返回值列表) {

golang 结构体中的匿名接口

golang 结构体中的匿名接口 代码示例 golang 中,可以给结构体增加匿名field,可参考 unknwon 大神的书. 匿名字段和内嵌结构体 但,golang同时也可以给结构体定义一个匿名interface field,用法: 标准库 sort 中,有下面的写法: type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) } type reverse struct { Interface } func

Golang中多用途的defer

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢? A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function returns, Golang的官方时这么定义的. 1.那么在什么情况下会调用defer延迟过的函数呢? 从文档中可以知道主要有两种情况: 当函数执行了return 语句后 当函数处于pan

golang中省略返回值造成内存泄漏

我已经两次因为不恰当的省略go中的函数返回值,一次造成MySql的too many connection错误,一次造成严重的内存泄漏.所以在这里大家分享一下这个问题和解决办法,也提醒自己以后不要再犯类似的错了. 众所周知,go中的函数可以返回多个值.但很多时候我们并不需要所有的值,而且go中定义了一个变量必须使用才可以,不然会报错.所以对于不需要的返回值,一般的操作方法就是省略: for _,value := range slice{ //.... } 一个典型就是上面的range.range可

在Golang中使用Redis

周五上班的主要任务是在公司老平台上用redis处理一个队列问题,顺便复习了一下redis操作的基础知识,回来后就想着在自己的博客demo里,用redis来优化一些使用场景,学习一下golang开发下redis的使用. Redis简单介绍 简介 关于Redis的讨论,其实在现在的后台开发中已经是个老生常谈的问题,基本上也是后端开发面试的基本考察点.其中 Redis的背景介绍和细节说明在这里就不赘述.不管怎么介绍,核心在于Redis是一个基于内存的key-value的多数据结构存储,并可以提供持久化

区块链技术语言(二十四)——Go语言面向对象:匿名组合

继承也是面向对象的三大基本特性之一.通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”.“父类”或“超类”.通过继承,不仅可以让某个类型的对象拥有属于自己的数据结构和操作,还会自动拥有父类的数据结构和操作.这使得现有的类在无需重新编写原来类的情况下对这些功能进行了扩展,很好地解决了代码的重用问题.继承机制的魅力和强大在于它允许程序员利用已经存在的类,并且可以以某种方式扩展这个类,而且对其他继承了这个父类属性和方法的对象没有影响.但Go语言没有继承这个概念,它通过匿名组合间接实现了

golang中接口interface和struct结构类的分析

再golang中,我们要充分理解interface和struct这两种数据类型.为此,我们需要优先理解type的作用. type是golang语言中定义数据类型的唯一关键字.对于type中的匿名成员和指针成员,这里先不讲,重点讲解interface和struct这两种特殊的数据类型. interface和struct也是数据类型,特殊在于interface作为万能的接口类型,而struct作为常用的自定义数据类型的关键字.说到这里相比大家已经明白interface的侧重点在于接口的定义(方法),

Golang 中关于闭包的坑

所谓闭包是指内层函数引用了外层函数中的变量或称为引用了自由变量的函数,其返回值也是一个函数,了解过的语言中有闭包概念的像 js,python,golang 都类似这样. python 中的闭包可以嵌套函数,像下面这样: def make_adder(addend): def adder(augend): return augend + addend return adder 转化成 golang 代码则像下面这样: func outer(x int) func(int) int{ func in

golang中interface接口的深度解析

什么是interface,简单的说,interface是一组method的组合,下面这篇文章主要给大家深度解析了关于golang中的interface接口,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧. 一 接口介绍 如果说gorountine和channel是支撑起Go语言的并发模型的基石,让Go语言在如今集群化与多核化的时代成为一道亮丽的风景,那么接口是Go语言整个类型系列的基石,让Go语言在基础编程哲学的探索上达到前所