温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在Golang中通过匿名组合实现伪继承

发布时间:2021-05-21 16:38:25 来源:亿速云 阅读:199 作者:Leah 栏目:编程语言

今天就跟大家聊聊有关怎么在Golang中通过匿名组合实现伪继承,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

1.匿名组合

1.1 匿名组合定义

golang中组合语法,就是在一个类中,引入了另一个类,如

  type Logger struct{
  }
  type Work struct{
    log Logger
  }
  type Work2 struct{
    log *Logger
  }

  func (Logger)Info(v ...interface{}){
  }

如上边代码所示,Work类中定义了一个Logger类型的变量,这种是比较常见的引入方式,姑且在此称之为非匿名组合,那什么是匿名组合呢,如其名,就是在组合的过程中,不给名字呗,如代码所示:

type Logger struct {
}
type Work struct {
  Logger
}
type Work2 struct {
  *Logger
}

func (Logger) Info(v ...interface{}) {
}

上边的代码中,Work类与Work2类均与Logger类匿名组合。两个类唯一不同的是,Work2中组合的是指针类型的Logger类。

1.2 组合对象初始化

非匿名组合初始化方式

func main(){
  var wk = Work{log:Logger{}}
  var wwk = Work{Logger{}}
  //...and so on

  var wk2 = Work2{log:new(Logger)}
  var wwk2 = Work2{new(Logger)}
  //... and so on
}

匿名组合初始化

func main(){
  var wk = Work{Logger{}}
  var wwk = Work{Logger:Logger{}}
  //... and so on
  var wk2 = Work2{new(Logger)}
  var wwk2 = Work2{Logger:&Logger{}}
  //... and so on
}

上边是匿名组合常见的初始化方式。匿名组合后,被包含类得方法和属性可以直接被使用,即使是私有变量。

注意事项:

1.匿名组合多个类时,不同的类存在相同的方法,会不会冲突?答案是,不同的类中,不同的方法时不会冲突的,但是在调用这个方法时,需要明确是那个类中的方法,如果匿名组合进来的类得方法,与这个类主体中的方法发生冲突,那么默认情况下,会使用主体类中的方法。

2.匿名组合多个类时,类名相同,会不会冲突?答案是,会。就算包名不同,类名相同,也会冲突。

示例代码:

package main
import(
  "bufio"
)
type Reader struct {
}
type Work4 struct {
  Reader
  bufio.Reader
}

上边代码编译时,会提示Reader重复定义 duplicate field Reader

原因在于,匿名组合中,没有给引入的类命名,所以默认采用了类名作为属性名。如上边wwk2这个对象在调用Logger的Info方法时,可以采用wwk2.Info(“hello”),也可以采用wwk2.Logger.Info(“hello”).

下边附上一段完整的演示代码,注意会报错哦,这段代码包含了上边的duplicate field Reader错误:

package main

import (
  "bufio"
  "fmt"
)

type Logger struct {
}

type Work struct {
  Logger
}

type Work2 struct {
  *Logger
}
type Work3 struct {
  log *Logger
}

type Reader struct {
}
type Work4 struct {
  Reader
  bufio.Reader
}

func (Logger) Info(v ...interface{}) {
  fmt.Println(v...)
}

func main() {
  var wk = Work{Logger{}}
  wk.Info("hello: Work{Logger{}}")
  var wwk = Work{Logger: Logger{}}
  wwk.Info("hello: Work{Logger: Logger{}}")
  //... and so on
  var wk2 = Work2{new(Logger)}
  wk2.Info("hello: Work2{new(Logger)}")
  var wwk2 = Work2{Logger: &Logger{}}
  wwk2.Info("hello: Work2{Logger: &Logger{}}")
  wwk2.Logger.Info("hello: wwk2.Logger.Info")

  var wk3 = Work3{new(Logger)}
  wk3.log.Info("hello: Work3{new(Logger)}")
}

3. 结构体嵌入和匿名成员

Go语言提供别样的 结构体嵌入 机制,让一个结构体包含另一个结构体类型的 匿名成员 , 这样就可以通过简单的点运算符x.f来访问匿名成员链中嵌套的x.d.e.f成员。

Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字; 这类成员就叫匿名成员。 匿名成员的数据类型必须是命名的(而不是匿名的)类型或指向一个命名的类型的指针。

type Circle struct {
 Point
 Radius int
} 

type Wheel struct {
 Circle
 Spokes int
}

由于有了匿名嵌入的特性, 我们可以直接访问内嵌类型的成员变量而不需要给出完整的路径:

var w Wheel
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 匿名冲突(duplicate field)

匿名成员也有一个隐式的名字,以其类型名称(去掉包名部分)作为成员变量的名字。 因此不能同一级同时包含两个类型相同的匿名成员, 这会导致名字冲突。

type Logger struct {
  Level int
}

type MyJob struct {
  *Logger
  Name string
  *log.Logger // duplicate field Logger
}

4. 匿名组合不是继承

4.1 方法的接受者没变

当我们嵌入一个类型,这个类型的方法就变成了外部类型的方法,但是当它被调用时,方法的接受者是内部类型(嵌入类型),而非外部类型。— Effective Go

type Job struct {
 Command string
 *log.Logger
}

func (job *Job)Start() {
 job.Log("starting now...")
 ... // 做一些事情
 job.Log("started.")
}

上面这个Job例子,即使组合后调用的方式变成了job.Log(...),但Log函数的接收者仍然是 log.Logger指针,因此在Log中也不可能访问到job的其他成员方法和变量。

4.1 内嵌类型不是基类

如果读者对基于 类 来实现的面向对象语言比较熟悉的话, 可能会倾向于将 内嵌类型 看作一个基类, 而 外部类型 看作其子类或者继承类, 或者将 外部类型 看作 "is a" 内嵌类型 。 但这样理解是错误的。

type Point struct{ X, Y float64 }

type ColoredPoint struct {
 Point
 Color color.RGBA
}

func (p Point) Distance(q Point) float64 {
 dX := q.X - p.X
 dY := q.Y - p.Y
 return math.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}
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
fmt.Println(p.Distance(q.Point)) // "5"

p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point

一个ColoredPoint并不是一个Point, 但ColoredPoint "has a" Point, 并且它有从Point类里引入的 Distance方法。

实际上,从实现的角度来考虑问题, 内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法, 和下面的形式是等价的:

func (p ColoredPoint) Distance(q Point) float64 {
 return p.Point.Distance(q)
}

当Point.Distance被以上编译器生成的包装方法调用时, 它的接收器值是p.Point, 而不是p。

4.3 匿名冲突(duplicate field) 和隐式名字

匿名成员也有一个隐式的名字,以其类型名称(去掉包名部分)作为成员变量的名字。 因此不能同一级同时包含两个类型相同的匿名成员, 这会导致名字冲突。

type Logger struct {
Level int
}

type MyJob struct {
*Logger
Name string
*log.Logger // duplicate field Logger
}

以下两点都间接说明匿名组合不是继承:

  • 匿名成员有隐式的名字

  • 匿名可能冲突(duplicate field)

什么是golang

golang 是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,其语法与 C语言相近,但并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能。

看完上述内容,你们对怎么在Golang中通过匿名组合实现伪继承有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI