Go语言不是一门面向对象的语言,没有对象和继承,也没有面向对象的多态、重写相关特性。
Go所拥有的是数据结构,它可以关联方法。Go也支持简单但高效的组合(Composition),请搜索面向对象和组合。
虽然Go不支持面向对象,但Go通过定义数据结构的方式,也能实现与Class相似的功能。
一个简单的例子,定义一个Animal数据结构:
type Animal struct {
name string
speak string
}
这就像是定义了一个class,有自己的属性。
在稍后,将会介绍如何向这个数据结构中添加方法,就像为类定义方法一样。不过现在,先简单介绍下数据结构。
数据结构的定义和初始化
除了int、string等内置的数据类型,我们可以定义structure来自定义数据类型。
创建数据结构最简单的方式:
bm_horse := Animal{
name:"baima",
speak:"neigh",
}
注意,上面最后一个逗号","不能省略,Go会报错,这个逗号有助于我们去扩展这个结构,所以习惯后,这是一个很好的特性。
上面bm_horse := Animal{}
中,Animal就像是一个类,这个声明和赋值的操作就像创建了一个Animal类的实例,也就是对象,其中对象名为bm_horse
,它是这个实例的唯一标识符。这个对象具有属性name和speak,它们是每个对象所拥有的key,且它们都有自己的值。从面向对象的角度上考虑,这其实很容易理解。
还可以根据Animal数据结构再创建另外一个实例:
hm_horse := Animal{
name:"heima",
speak:"neigh",
}
bm_horse
和hm_horse
都是Animal的实例,根据Animal数据结构创建而来,这两个实例都拥有自己的数据结构。如下图:
从另一种角度上看,bm_horse
这个名称其实是这个数据结构的一个引用。再进一步考虑,其实面向对象的类和对象也是一种数据结构,每一个对象的名称(即bm_horse
)都是对这种数据结构的引用。关于这一点,在后面介绍指针的时候将非常有助于理解。
以下是两外两种有效的数据结构定义方式:
// 定义空数据结构
bm_horse := Animal{}
// 或者,先定义一部分,再赋值
bm_horse := Animal {name:"baima"}
bm_horse.speak = "neigh"
此外,还可以省略数据结构中的key部分(也就是属性的名称)直接为数据结构中的属性赋值,只不过这时赋的值必须和key的顺序对应。
bm_horse := Animal{"baima","neigh"}
在数据结构的属性数量较少的时候,这种赋值方式也是不错的,但属性数量多了,不建议如此赋值,因为很容易混乱。
访问数据结构的属性
要访问一个数据结构中的属性,如下:
package main
import ("fmt")
func main(){
type Animal struct {
name string
speak string
}
bm_horse := Animal{"baima","neigh"}
fmt.Println("name:",bm_horse.name)
fmt.Println("speak:",bm_horse.speak)
}
前面说过,Animal是一个数据结构的模板(就像类一样),不是实例,bm_horse
才是具体的实例,有自己的数据结构,所以,要访问自己数据结构中的数据,可以通过自己的名称来访问自己的属性:
bm_horse.name
bm_horse.speak
指针
bm_horse := Animal{}
表示返回一个数据结构给bm_horse,bm_horse指向这个数据结构,也可以说bm_horse是这个数据结构的引用。
除此,还有另一种赋值方式,比较下两种赋值方式:
bm_horse := Animal{"baima","neigh"}
ref_bm_horse := &Animal{"baima","neigh"}
这两种赋值方式,有何不同?
:=
操作符都声明左边的变量,并赋值变量。赋值的内容基本神似:
- 第一种将整个数据结构赋值给变量
bm_horse
,bm_horse
从此变成Animal的实例;
- 第二种使用了一个特殊符号
&
在数据结构前面,它表示返回这个数据结构的引用,也就是这个数据结构的地址,所以ref_bm_horse
也指向这个数据结构。
那bm_horse
和ref_bm_horse
都指向这个数据结构,有什么区别?我打算用perl语言的语法来解释它们的区别,因为C和Go的指针太过"晦涩"。
perl中的引用
在Perl中,一个hash结构使用%
符号来表示,例如:
%Animal = (
name => "baima",
speak => "neigh",
);
这里的"Animal"表示的是这个hash结构的名称,然后通过%+NAME
的方式来引用这个hash数据结构。其实hash结构的名称"Animal"就是这个hash结构的一个引用,表示指向这个hash结构,只不过这个Animal
是创建hash结构是就指定好的已命名的引用。
perl中还支持显式地创建一个引用。例如:
$ref_myhash = \%Animal;
%Animal
表示的是hash数据结构,加上\
表示这个数据结构的一个引用,这个引用指向这个hash数据结构。perl中的引用是一个变量,所以使用$ref_myhash
表示。
也就是说,hash结构的名称Animal
和$ref_myhash
是完全等价的,都是hash结构的引用,也就是指向这个数据结构,也就是指针。所以,%Animal
能表示取hash结构的属性,%$ref_myhash
也能表示取hash结构的属性,这种从引用取回hash数据结构的方式称为"解除引用"。
另外,$ref_myhash
是一个变量类型,而%Animal
是一个hash类型。
引用变量可以赋值给另一个引用变量,这样两个引用都将指向同一个数据结构:
$ref_myhash1 = $ref_myhash;
现在,$ref_myhash
、$ref_myhash1
和Animal
都指向同一个数据结构。
Go中的指针:引用
总结下上面perl相关的代码:
%Animal = (
name => "baima",
speak => "neigh",
);
$ref_myhash = \%Animal;
$ref_myhash1 = $ref_myhash;
%Animal
是hash结构,Ani