Golang中匿名组合实现伪继承的方法
"Go语言的面向对象机制与一般语言不同。它没有类层次结构,甚至可以说没有类;仅仅通过组合(而不是继承)简单的对象来构建复杂的对象。"--《Go语言圣经》
1.匿名组合
1.1匿名组合定义
golang中组合语法,就是在一个类中,引入了另一个类,如
typeLoggerstruct{ } typeWorkstruct{ logLogger } typeWork2struct{ log*Logger } func(Logger)Info(v...interface{}){ }
如上边代码所示,Work类中定义了一个Logger类型的变量,这种是比较常见的引入方式,姑且在此称之为非匿名组合,那什么是匿名组合呢,如其名,就是在组合的过程中,不给名字呗,如代码所示:
typeLoggerstruct{ } typeWorkstruct{ Logger } typeWork2struct{ *Logger } func(Logger)Info(v...interface{}){ }
上边的代码中,Work类与Work2类均与Logger类匿名组合。两个类唯一不同的是,Work2中组合的是指针类型的Logger类。
1.2组合对象初始化
非匿名组合初始化方式
funcmain(){ varwk=Work{log:Logger{}} varwwk=Work{Logger{}} //...andsoon varwk2=Work2{log:new(Logger)} varwwk2=Work2{new(Logger)} //...andsoon }
匿名组合初始化
funcmain(){ varwk=Work{Logger{}} varwwk=Work{Logger:Logger{}} //...andsoon varwk2=Work2{new(Logger)} varwwk2=Work2{Logger:&Logger{}} //...andsoon }
上边是匿名组合常见的初始化方式。匿名组合后,被包含类得方法和属性可以直接被使用,即使是私有变量。
注意事项:
1.匿名组合多个类时,不同的类存在相同的方法,会不会冲突?答案是,不同的类中,不同的方法时不会冲突的,但是在调用这个方法时,需要明确是那个类中的方法,如果匿名组合进来的类得方法,与这个类主体中的方法发生冲突,那么默认情况下,会使用主体类中的方法。
2.匿名组合多个类时,类名相同,会不会冲突?答案是,会。就算包名不同,类名相同,也会冲突。
示例代码:
packagemain import( "bufio" ) typeReaderstruct{ } typeWork4struct{ Reader bufio.Reader }
上边代码编译时,会提示Reader重复定义duplicatefieldReader
原因在于,匿名组合中,没有给引入的类命名,所以默认采用了类名作为属性名。如上边wwk2这个对象在调用Logger的Info方法时,可以采用wwk2.Info(“hello”),也可以采用wwk2.Logger.Info(“hello”).
下边附上一段完整的演示代码,注意会报错哦,这段代码包含了上边的duplicatefieldReader错误:
packagemain import( "bufio" "fmt" ) typeLoggerstruct{ } typeWorkstruct{ Logger } typeWork2struct{ *Logger } typeWork3struct{ log*Logger } typeReaderstruct{ } typeWork4struct{ Reader bufio.Reader } func(Logger)Info(v...interface{}){ fmt.Println(v...) } funcmain(){ varwk=Work{Logger{}} wk.Info("hello:Work{Logger{}}") varwwk=Work{Logger:Logger{}} wwk.Info("hello:Work{Logger:Logger{}}") //...andsoon varwk2=Work2{new(Logger)} wk2.Info("hello:Work2{new(Logger)}") varwwk2=Work2{Logger:&Logger{}} wwk2.Info("hello:Work2{Logger:&Logger{}}") wwk2.Logger.Info("hello:wwk2.Logger.Info") varwk3=Work3{new(Logger)} wk3.log.Info("hello:Work3{new(Logger)}") }
3.结构体嵌入和匿名成员
Go语言提供别样的结构体嵌入机制,让一个结构体包含另一个结构体类型的匿名成员,这样就可以通过简单的点运算符x.f来访问匿名成员链中嵌套的x.d.e.f成员。
Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员。匿名成员的数据类型必须是命名的(而不是匿名的)类型或指向一个命名的类型的指针。
typeCirclestruct{ Point Radiusint } typeWheelstruct{ Circle Spokesint }
由于有了匿名嵌入的特性,我们可以直接访问内嵌类型的成员变量而不需要给出完整的路径:
varwWheel w.X=8//等价于w.Circle.Point.X=8 w.Y=8//等价于w.Circle.Point.Y=8 w.Radius=5//等价于w.Circle.Radius=5 w.Spokes=20
同样的规则,内嵌类型的方法也会提升为外部类型的方法。
3.1匿名冲突(duplicatefield)
匿名成员也有一个隐式的名字,以其类型名称(去掉包名部分)作为成员变量的名字。因此不能同一级同时包含两个类型相同的匿名成员,这会导致名字冲突。
typeLoggerstruct{ Levelint } typeMyJobstruct{ *Logger Namestring *log.Logger//duplicatefieldLogger }
4.匿名组合不是继承
4.1方法的接受者没变
当我们嵌入一个类型,这个类型的方法就变成了外部类型的方法,但是当它被调用时,方法的接受者是内部类型(嵌入类型),而非外部类型。—EffectiveGo
typeJobstruct{ Commandstring *log.Logger } func(job*Job)Start(){ job.Log("startingnow...") ...//做一些事情 job.Log("started.") }
上面这个Job例子,即使组合后调用的方式变成了job.Log(...),但Log函数的接收者仍然是log.Logger指针,因此在Log中也不可能访问到job的其他成员方法和变量。
4.1内嵌类型不是基类
如果读者对基于类来实现的面向对象语言比较熟悉的话,可能会倾向于将内嵌类型看作一个基类,而外部类型看作其子类或者继承类,或者将外部类型看作"isa"内嵌类型。但这样理解是错误的。
typePointstruct{X,Yfloat64} typeColoredPointstruct{ Point Colorcolor.RGBA } func(pPoint)Distance(qPoint)float64{ dX:=q.X-p.X dY:=q.Y-p.Y returnmath.Sqrt(dX*dX+dY*dY) }
请注意上面例子中对Distance方法的调用。Distance有一个参数是Point类型,但q并不是一个Point类,所以尽管q有着Point这个内嵌类型,我们也必须要显式地选择它。尝试直接传q的话你会看到错误:
red:=color.RGBA{255,0,0,255} blue:=color.RGBA{0,0,255,255} varp=ColoredPoint{Point{1,1},red} varq=ColoredPoint{Point{5,4},blue} fmt.Println(p.Distance(q.Point))//"5" p.Distance(q)//compileerror:cannotuseq(ColoredPoint)asPoint
一个ColoredPoint并不是一个Point,但ColoredPoint"hasa"Point,并且它有从Point类里引入的Distance方法。
实际上,从实现的角度来考虑问题,内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法,和下面的形式是等价的:
func(pColoredPoint)Distance(qPoint)float64{ returnp.Point.Distance(q) }
当Point.Distance被以上编译器生成的包装方法调用时,它的接收器值是p.Point,而不是p。
4.3匿名冲突(duplicatefield)和隐式名字
匿名成员也有一个隐式的名字,以其类型名称(去掉包名部分)作为成员变量的名字。因此不能同一级同时包含两个类型相同的匿名成员,这会导致名字冲突。
typeLoggerstruct{ Levelint } typeMyJobstruct{ *Logger Namestring *log.Logger//duplicatefieldLogger }
以下两点都间接说明匿名组合不是继承:
- 匿名成员有隐式的名字
- 匿名可能冲突(duplicatefield)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。