Go 面向对象式编程

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

Go 语言没有对象的概念,可是 struct 类型有着和对象相似的特性。struct 类型能够定义本身的属性和方法。这篇文章咱们来总结下 Go 语言中关于 “继承” 和多态的概念。函数

嵌入类型

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

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 的方法。学习

经过嵌入,内部类型的属性、方法,能够为外部类型全部,就好像是外部类型本身的同样。此外,外部类型还能够定义本身的属性和方法,甚至能够定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。spa

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

输出:.net

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:"Jack@gmail.com",
		},
		Level:"admin",
	}
	admin.Speak()   // 方式一
	admin.User.Speak()  // 方式二
}
复制代码

输出:code

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

能够看到,Admin 定义了本身的 Speak() 方法时,会自动调用本身的方法,而屏蔽内部类型的方法。对于属性也是同样的状况。
查看完整代码cdn

另外,更重要的是,若是内部类型实现接口 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: "Jack@gmail.com",
		},
		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: "Jack@gmail.com",
		},
		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 语言相关书籍,公号后台回复【电子书】领取!

公众号二维码
相关文章
相关标签/搜索