面向对象编程
对于面向对象编程Go语言设计得非常简洁而优雅。简洁之处在于,Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。优雅之处在于,Go语言对面向对象编程的支持是语言类型系统中的天然组成部分,整个类型系统通过接口串联,浑然一体。
类型系统
其他语言很少涉及类型系统(type system)这方面知识,实际上类型语言才是一门编程语言的地基,它的重要程度毋庸置疑。
顾名思义,类型系统是指一个语言的类型体系结构。一个典型的类型系统通常包含如下基本内容:
- 基础类型,如byte、int、bool、float等
- 复合类型,如数组、结构体、指针等
- 可以指向任意对象的类型(Any类型)
- 值语义和引用语义
- 面向对象,即所有具备面向对象特征(比如成员方法)的类型
- 接口 Java语言自诞生以来被称为最纯正的面向对象语言,在java语言中存在两套独立的类型系统,一种是值类型系统,主要是基本类型,如byte、int、boolean、char、double等。一套是以Object类型为根的对象类型系统,这些类型可以是成员变量和成员方法,是基于引用语义,只允许在堆上创建,java有种万物皆对象的说法,可以这样理解Object是所以类的父类,我们在使用类的时候就会利用Object类中的方法。
相比之下,Go语言中的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要的时候,你可以给任何类型(包括内置类型)“增加”新方法。而在实现某个接口时,无需从该接口继承,只需要实现该接口要求的所有方法即可。任何类型都可以被Any类型引用。Any类型就是空接口,即interface{}。
为类添加方法
在Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法,如下:
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
定义了一个新类型Integer,它和int没有本质不同,只是它为内置的int类型增加了个新方法Less()。但是吧说老实话这种操作搞得我有点茫然,没懂这样做的目的是什么?实现了Integer后,就可以让整型像一个普通的类一样使用?
func main() {
var a Integer = 1
if a.Less(2) {
fmt.Println(a, "Less 2")
}
}
上面的这个Integer例子如果不使用Go语言的面向对象特性,而使用面向过程方式实现的话,相应的实现细节将如下所示:
type Integer int
func Integer_Less(a Integer, b Integer) bool {
return a < b
}
func main() {
var a Integer = 1
if Integer_Less(a, 2) {
fmt.Println(a, "Less 2")
}
}
在Go语言中,面向对象的神秘面纱被剥得一干二净。对比下面的两段代码:
func (a Integer) Less(b Integer) bool { // 面向对象
return a < b
}
func Integer_Less(a Integer, b Integer) bool { // 面向过程
return a < b
}
a.Less(2) // 面向对象的用法
Integer_Less(a, 2) // 面向过程的用法
面向对象只是换了一种语法形式来表达。C++语言的面向对象之所以让有些人迷惑的一大原因就在于其隐藏的this指针。一旦把隐藏的this指针显露出来,大家看到的就是一个面向过程编程。
“在Go语言中没有隐藏的this指针”这句话的含义是:
- 方法施加的目标(也就是“对象”)显式传递,没有被隐藏起来
- 方法施加的目标(也就是“对象”)不需要非得是指针,也不用非得叫this 比如java这种代码
class Integer {
private int val;
public boolean Less(Integer b) {
return this.val< b.val;
}
}
第一次看这种代码会不明白this.val是从哪来的,那么这种代码如果面向过程书写c语言实现可能会好理解,如下:
struct Integer {
int val;
};
bool Integer_Less(Integer* this, Integer* b) {
return this->val < b->val;
}
可以看出,java代码将Integer类的方法Less()的第一个参数Integer* this被隐藏起来了,C语言的实现显示的给出了Integer* this。
在如果需要修改对象的时候,必须用指针传递,它不是Go语言的约束,而是一种自然约束。如下:
func (a *Integer) Add(b Integer) {
*a += b
}
Integer类型增加了Add()方法,该方法需要修改对象的值,必须所以需要用指针引用,调用如下
func main() {
var a Integer = 1
a.Add(2)
fmt.Println("a =", a)
}
运行结果为a = 3,改变了对象的引用,如果不引用传递,是值传递的话
func (a Integer) Add(b Integer) {
a += b
}
运行程序得到的结果是a = 1,也就是维持原来的值。究其原因,是因为Go和C语言一样,类型都是基于值传递。要想修改变量的值,只能传递指针。如果值传递原值不会被修改!java当中值传递可以认为是复制了一个相同的值,对其操作不会影响复制前a的值,所以a的值维持不变。
总结
总结了Go语言面向对象编程,舍弃了其他语言繁杂的实现,只沿用了实现接口中所有的方法,总结了值传递和引用传递的区别。
备注
师傅我快坚持不住了好瞌睡!!!!滚去睡觉了拜拜!!!
本文正在参与「掘金Golang主题学习月」, 点击查看活动详情。