咱们晓得Go中没有继承,接口的用法也与Java中的用法天壤之别,很多适宜,咱们须要应用OO的思维来组织咱们达到我的项目,然而将Java的oo思维在Go中会比拟苦楚,Go中的办法和面向对象的相似,然而定义上也是很多初学者不好了解的,比方办法的定义居然在构造体内部,组装起来看起来也很随便,其实Go只是有面向对象个性,其实不算是面向对象语言。
办法
Go反对一些面向对象编程个性,办法是这些所反对的个性之一。 本篇文章将介绍在Go中和办法相干的各种概念
办法申明
在Go中,咱们能够为类型T
和*T
显式地申明一个办法,其中类型T
必须满足四个条件:
T
必须是一个定义类型;T
必须和此办法申明定义在同一个代码包中;T
不能是一个指针类型;T
不能是一个接口类型。
类型T
和*T
称为它们各自的办法的属主类型(receiver type)。 类型T
被称作为类型T
和*T
申明的所有办法的属主基类型(receiver base type)
如果咱们为某个类型申明了一个办法,当前咱们能够说此类型领有此办法,一个办法申明和一个函数申明很类似,然而比函数申明多了一个额定的参数申明局部。 此额定的参数申明局部只能含有一个类型为此办法的属主类型的参数,此参数称为此办法申明的属主参数(receiver parameter)。 此属主参数申明必须包裹在一对小括号()之中。 此属主参数申明局部必须处于func关键字和办法名之间
上面是一个办法申明的例子:
// Age和int是两个不同的类型。咱们不能为int和*int// 类型申明办法,然而能够为Age和*Age类型申明办法。type Age intfunc (age Age) LargerThan(a Age) bool { return age > a}func (age *Age) Increase() { *age++}// 为自定义的函数类型FilterFunc申明办法。type FilterFunc func(in int) boolfunc (ff FilterFunc) Filte(in int) bool { return ff(in)}// 为自定义的映射类型StringSet申明办法。type StringSet map[string]struct{}func (ss StringSet) Has(key string) bool { _, present := ss[key] return present}func (ss StringSet) Add(key string) { ss[key] = struct{}{}}func (ss StringSet) Remove(key string) { delete(ss, key)}// 为自定义的构造体类型Book和它的指针类型*Book申明办法。type Book struct { pages int}func (b Book) Pages() int { return b.pages}func (b *Book) SetPages(pages int) { b.pages = pages}
每个办法对应着一个隐式申明的函数
对每个办法申明,编译器将主动隐式申明一个绝对应的函数。 比方对于上一节的例子中为类型Book
和*Book
申明的两个办法,编译器将主动申明上面的两个函数:
func Book.Pages(b Book) int { return b.pages // 此函数体和Book类型的Pages办法体一样}func (*Book).SetPages(b *Book, pages int) { b.pages = pages // 此函数体和*Book类型的SetPages办法体一样}
在下面的两个隐式函数申明中,它们各自对应的办法申明的属主参数申明被插入到了一般参数申明的第一位。 它们的函数体和各自对应的显式办法的办法体是一样的。
两个隐式函数名Book.Pages
和(*Book).SetPages
都是aType.MethodName
这种模式的。 咱们不能显式申明名称为这种模式的函数,因为这种模式不属于非法标识符。这样的函数只能由编译器隐式申明。 然而咱们能够在代码中调用这些隐式申明的函数
办法原型(method prototype)和办法集(method set)
一个办法原型能够看作是一个不带func关键字的函数原型。 咱们能够把每个办法申明看作是由一个func关键字、一个属主参数申明局部、一个办法原型和一个办法体组成
办法值和办法调用
办法事实上是非凡的函数。办法也常被称为成员函数。 当一个类型领有一个办法,则此类型的每个值将领有一个不可批改的函数类型的成员(相似于构造体的字段)。 此成员的名称为此办法名,它的类型和此办法的申明中不包含属主局部的函数申明的类型统一。 一个值的成员函数也能够称为此值的办法。
一个办法调用其实是调用了一个值的成员函数。假如一个值v
有一个名为m
的办法,则此办法能够用选择器语法模式v.m
来示意。
上面这个例子展现了如何调用为Book
和*Book
类型申明的办法:
package mainimport "fmt"type Book struct { pages int}func (b Book) Pages() int { return b.pages}func (b *Book) SetPages(pages int) { b.pages = pages}func main() { var book Book fmt.Printf("%T \n", book.Pages) // func() int fmt.Printf("%T \n", (&book).SetPages) // func(int) // &book值有一个隐式办法Pages。 fmt.Printf("%T \n", (&book).Pages) // func() int // 调用这三个办法。 (&book).SetPages(123) book.SetPages(123) // 等价于上一行 fmt.Println(book.Pages()) // 123 fmt.Println((&book).Pages()) // 123}
和一般参数传参一样,属主参数的传参也是一个值复制过程。 所以,在办法体内对属主参数的间接局部的批改将不会反映到办法体外
参考
更多具体内容能够查看 https://gfw.go101.org/article/101.html
吴邪,小三爷,混迹于后盾,大数据,人工智能畛域的小菜鸟。
更多请关注