在Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type Integer int func (a Integer) Less(b Integer) bool { return a < b } func main () { var a Integer = 1 if a.Less(2 ) { fmt.Println(a, "Less 2" ) } } func (a Integer) Less(b Integer) bool { } func Integer_Less (a Integer, b Integer) bool { } a.Less(2 ) Integer_Less(a, 2 )
C++语言的面向对象之所以让有些人迷惑的一大原因就在于其隐藏的this指针。一旦把隐藏的this指针显露出来,大家看到的就是一个面向过程编程。如果读者了解Python语法,就会知道Python的成员方法中会有一个self参数,它和this指针的作用是完全一样的。
1 2 3 4 5 struct Integer { int val;}; bool Integer_Less (Integer* this, Integer* b) { return this->val < b->val; }
go传递指针而不是值的写法:
1 2 3 func (a *Integer) Add(b Integer) { *a += b }
C语言中的数组比较特别。通过函数传递一个数组的时候基于引用语义,但是在结构体中定义数组变量的时候基于值语义(表现在为结构体赋值的时候,该数组会被完整地复制)。Go语言中的数组和基本类型没有区别,是很纯粹的值类型
1 2 3 4 5 6 7 8 9 var a = [3 ]int {1 , 2 , 3 } var b = ab[1 ]++ fmt.Println(a, b) var a = [3 ]int {1 , 2 , 3 } var b = &ab[1 ]++ fmt.Println(a, *b)
类的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 type Rect struct { x, y float64 width, height float64 } func NewRect (x, y, width, height float64 ) *Rect { return &Rect{x, y, width, height} } rect1 := new (Rect) rect2 := &Rect{} rect3 := &Rect{0 , 0 , 100 , 200 } rect4 := &Rect{width: 100 , height: 200 }
Go语言也提供了继承,但是采用了组合的文法,所以我们将其称为匿名组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type Base struct { Name string } func (base *Base) Foo() { ... }func (base *Base) Bar() { ... }type Foo struct { Base ... } func (foo *Foo) Bar() { foo.Base.Bar() ... } type Foo struct { ... Base } type Foo struct { *Base ... }
Go语言中符号的可访问性是包一级的而不是类型一级的。如果Go语言符号的可访问性是类型一级的,少不了还要加上friend这样的关键字,以表示两个类是朋友关系,可以访问彼此的私有成员。
在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 type File struct { } func (f *File) Read(buf []byte ) (n int , err error )func (f *File) Write(buf []byte ) (n int , err error )func (f *File) Seek(off int64 , whence int ) (pos int64 , err error ) func (f *File) Close() error type IFile interface { Read(buf []byte ) (n int , err error ) Write(buf []byte ) (n int , err error ) Seek(off int64 , whence int ) (pos int64 , err error ) Close() error } type IReader interface { Read(buf []byte ) (n int , err error ) } type IWriter interface { Write(buf []byte ) (n int , err error ) } type ICloser interface { Close() error } var file1 IFile = new (File)var file2 IReader = new (File)var file3 IWriter = new (File)var file4 ICloser = new (File)
Go语言可以根据下面的函数: func (a Integer) Less(b Integer) bool 自动生成一个新的Less()方法,类型*Integer就既存在Less()方法,也存在Add()方法,满足LessAdder接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type Integer int func (a Integer) Less(b Integer) bool { return a < b } func (a *Integer) Add(b Integer) { *a += b } type LessAdder interface { Less(b Integer) bool Add(b Integer) } func (a *Integer) Less(b Integer) bool { return (*a).Less(b) } func (a Integer) Add(b Integer) { (&a).Add(b) } var a Integer = 1 var b LessAdder = &a var b LessAdder = a
在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值。接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集, 么接口B可以赋值给接口A。
检查file1接口指向的对象实例是否实现了two.IStream接口,如果实现了,则执行特定的代码。接口查询是否成功,要在运行期才能够确定。它不像接口赋值,编译器只需要通过静态类型检查即可判断赋值是否可行。
1 2 3 4 var file1 Writer = ...if file5, ok := file1.(two.IStream); ok { ... }
在Go语言中,你可以询问接口它指向的对象是否是某个类型,比如
1 2 3 4 var file1 Writer = ...if file6, ok := file1.(*File); ok {... }
接口组合,可以认为接口组合是类型匿名组合的一个特定场景,只不过接口只包含方法,而不包含任何成员变量。
1 2 3 4 5 6 7 8 9 10 type ReadWriter interface { Reader Writer } type ReadWriter interface { Read(p []byte ) (n int , err error ) Write(p []byte ) (n int , err error ) }