24

Go 面向对象式编程

 4 years ago
source link: https://www.tuicool.com/articles/u2euA3B
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

『就要学习 Go 语言』系列 -- 第 25 篇分享好文

Go 语言没有对象的概念,但是 struct 类型有着和对象类似的特性。struct 类型可以定义自己的属性和方法。这篇文章我们来总结下 Go 语言中关于 “继承” 和多态的概念。

嵌入类型

嵌入类型是指将已有的类型直接声明在新的结构类型里。不像 Java、C++ 等语言,Go 语言没有继承,但是可以通过 组合 的方式实现代码的复用。

type User struct {
	Name string
	Email string
}

type Admin struct {
	User
	Level string
}

func (u *User) Speak()  {
	fmt.Println("I am user",u.Name)
}
复制代码

上面的代码定义了两个结构体 User 和 Admin,Admin 有一个匿名成员 User,因为是匿名,所以类型即名称。将 User 嵌入 Admin,Admin 是被嵌入的类型,也称 外部类型 ,User 是 内部类型 。Speak() 是 User 的方法。

通过嵌入,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。

admin := Admin{
		User:User{
			Name:"Jack",
			Email:"[email protected]",
		},
		Level:"admin",
	}
	// 内部类型的方法也被提升到外部类型
	admin.Speak()   // 方式一
	// 直接访问内部类型的方法
	admin.User.Speak()  // 方式二
复制代码

输出:

I am user Jack
I am user Jack
复制代码

给 Admin 定义自己的 Speak() 方法:

func (a *Admin) Speak()  {
	fmt.Println("I am admin",a.Name)
}

func main()  {
	admin := Admin{
		User:User{
			Name:"Jack",
			Email:"[email protected]",
		},
		Level:"admin",
	}
	admin.Speak()   // 方式一
	admin.User.Speak()  // 方式二
}
复制代码

输出:

I am admin Jack
I am user Jack
复制代码

可以看到,Admin 定义了自己的 Speak() 方法时,会自动调用自己的方法,而屏蔽内部类型的方法。对于属性也是一样的情况。

查看完整 代码

另外,更重要的是, 如果内部类型实现接口 A,也可以认为外部类型也实现了接口 A。

type Speaker interface {
	Speak()   // 方法
}

func gotoSpeak(s Speaker) {
	s.Speak()
}
复制代码

定义了 Speaker 接口,任意类型如果实现了接口中定义的全部方法,就认为该类型实现了接口。例如,上面定义的 User 类型,就实现了接口 Speaker。gotoSpeak() 函数是接收 Speaker 接口类型的参数,任何实现了 Speaker 接口的类型都可以调用该函数。

admin := Admin{
		User: User{
			Name:  "Jack",
			Email: "[email protected]",
		},
		Level: "admin",
	}
	gotoSpeak(&admin)
复制代码

输出:

I am user Jack
复制代码

注意,关键点来了,调用 gotoSpeak() 时传的参数是 admin 的地址,类型是 *Admin,不能传 Admin 类型的值。从上篇文章我们知道,Admin 类型的方法集中不包括 Speak() 方法,也就是说 Admin 类型没有实现 Speaker 接口。

结合上篇关于类型方法集,对于嵌入类型的内部类型方法的提升可以总结下。假设 外部结构体类型是 S,内部类型是 T ,则关于内部类型的方法提升如下规则:

  1. T 嵌入 S,外部类型 S 可以通过值类型或指针类型调用内部类型 T 的值方法;
  2. T 嵌入 S,外部类型 S 只能通过指针类型调用内部类型 T 的指针方法;
  3. *T 嵌入 S,外部类型 S 可以通过值类型和指针类型调用内部类型 T 的值方法和指针方法;

上面的三条规则可以总结成一句话: 不管是 T 嵌入 S,还是 *T 嵌入 S,外部类型 S 唯独通过值类型不能调用内部类型 T 的指针方法外,其他情况下内部类型 T 的方法都可以获得提升,即可被外部类型 S 访 问 。

前两点其实很好理解,第三点是通过 指针方式组合 ,其实就是在外部类型初始化的时候,取得内部类型的指针。其他规则与非指针方式组合一致。

type Admin struct {
	*User            // 通过指针方式组合
	Level string
}

func main() {
	admin := Admin{
		User: &User{
			Name:  "Jack",
			Email: "[email protected]",
		},
		Level: "admin",
	}
	gotoSpeak(&admin)
	gotoSpeak(admin)
}
复制代码

输出:

I am user Jack
I am user Jack
复制代码

多态

其实上面的例子已经给出多态的例子了,这个给大家提一下。在 Go 语言中,每种类型都是不同的,但不同的类型可以实现同一接口,将它们绑定在同一接口上,用作函数或者放的输入(输出)参数。例如上面的 User 类型和 Admin 类型就是通过 Speaker 接口建立了关系。

深入阅读:

1. 教女朋友写方法(续)

2. Polymorphism - OOP in Go

3. Is Go An Object Oriented Language?

4.《Go 语言实战》5.4 5.5 节

(全文完)

原创文章,若需转载请注明出处!

欢迎扫码关注公众号「 Golang来啦 」或者移步 seekload.net ,查看更多精彩文章。

给你准备了学习 Go 语言相关书籍,公号后台回复【电子书】领取!

uABnIza.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK