GoLang之方法与接口
本文内容纲要:
-GoLang之方法与接口
-方法
-匿名组合
-值语义和引用语义
-接口
-空接口
-嵌入接口
GoLang之方法与接口
Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。
方法
Go语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。
如下面的这个例子,定义了一个新类型Integer,它和int一样,只是为它内置的int类型增加了个新方法Less()
typeIntegerint
func(aInteger)Less(bInteger)bool{
returna<b
}
funcmain(){
varaInteger=1
ifa.Less(2){
fmt.Println("lessthen2")
}
}
可以看出,Go语言在自定义类型的对象中没有C++/Java那种隐藏的this指针,而是在定义成员方法时显式声明了其所属的对象。
method的语法如下:
func(rReceiverType)funcName(parameters)(results)
当调用method时,会将receiver作为函数的第一个参数:
funcName(r,parameters);
所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的值,就需要传递对象的指针。
指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
func(a*Ingeger)Add(bInteger){
*a+=b
}
funcmain(){
varaInteger=1
a.Add(3)
fmt.Println("a=",a)//a=4
}
如果Add方法不使用指针,则a返回的结果不变,这是因为Go语言函数的参数也是基于值传递。
注意:当方法的接受者是指针时,即使用值类型调用那么方法内部也是对指针的操作。
之前说过,Go语言没有构造函数的概念,通常使用一个全局函数来完成。例如:
funcNewRect(x,y,width,heightfloat64)*Rect{
return&Rect{x,y,width,height}
}
funcmain(){
rect1:=NewRect(1,2,10,20)
fmt.Println(rect1.width)
}
匿名组合
Go语言提供了继承,但是采用了组合的语法,我们将其称为匿名组合,例如:
typeBasestruct{
namestring
}
func(base*Base)Set(mynamestring){
base.name=myname
}
func(base*Base)Get()string{
returnbase.name
}
typeDerivedstruct{
Base
ageint
}
func(derived*Derived)Get()(nmstring,agint){
returnderived.name,derived.age
}
funcmain(){
b:=&Derived{}
b.Set("sina")
fmt.Println(b.Get())
}
例子中,在Base类型定义了get()和set()两个方法,而Derived类型继承了Base类,并改写了Get()方法,在Derived对象调用Set()方法,会加载基类对应的方法;而调用Get()方法时,加载派生类改写的方法。
组合的类型和被组合的类型包含同名成员时,会不会有问题呢?可以参考下面的例子:
typeBasestruct{
namestring
ageint
}
func(base*Base)Set(mynamestring,myageint){
base.name=myname
base.age=myage
}
typeDerivedstruct{
Base
namestring
}
funcmain(){
b:=&Derived{}
b.Set("sina",30)
fmt.Println("b.name=",b.name,"\tb.Base.name=",b.Base.name)
fmt.Println("b.age=",b.age,"\tb.Base.age=",b.Base.age)
}
值语义和引用语义
值语义和引用语义的差别在于赋值,比如
b=a
b.Modify()
如果b的修改不会影响a的值,那么此类型属于值类型;如果会影响a的值,那么此类型是引用类型。
Go语言中的大多数类型都基于值语义,包括:
- 基本类型,如byte、int、bool、float32、string等;
- 复合类型,如arry、struct、pointer等;
C语言中的数组比较特别,通过函数传递一个数组的时候基于引用语义,但是在结构体定义数组变量的时候基于值语义。而在Go语言中,数组和基本类型没有区别,是很纯粹的值类型,例如:
vara=[3]int{1,2,3}
varb=a
b[1]++
fmt.Println(a,b)//[123][133]
从结果看,b=a赋值语句是数组内容的完整复制,要想表达引用,需要用指针:
vara=[3]int{1,2,3}
varb=&a//引用语义
b[1]++
fmt.Println(a,b)//[133][133]
接口
Interface是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合,如果实现了interface中的所有方法,即该类/对象就实现了该接口。
Interface的声明格式:
typeinterfaceNameinterface{
//方法列表
}
Interface可以被任意对象实现,一个类型/对象也可以实现多个interface;
interface的变量可以持有任意实现该interface类型的对象。
如下面的例子:
packagemain
import"fmt"
typeHumanstruct{
namestring
ageint
phonestring
}
typeStudentstruct{
Human//匿名字段
schoolstring
loanfloat32
}
typeEmployeestruct{
Human//匿名字段
companystring
moneyfloat32
}
//Human实现SayHi方法
func(hHuman)SayHi(){
fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)
}
//Human实现Sing方法
func(hHuman)Sing(lyricsstring){
fmt.Println("Lalalala...",lyrics)
}
//Employee重载Human的SayHi方法
func(eEmployee)SayHi(){
fmt.Printf("Hi,Iam%s,Iworkat%s.Callmeon%s\n",e.name,
e.company,e.phone)
}
//InterfaceMen被Human,Student和Employee实现
//因为这三个类型都实现了这两个方法
typeMeninterface{
SayHi()
Sing(lyricsstring)
}
funcmain(){
mike:=Student{Human{"Mike",25,"222-222-XXX"},"MIT",0.00}
paul:=Student{Human{"Paul",26,"111-222-XXX"},"Harvard",100}
sam:=Employee{Human{"Sam",36,"444-222-XXX"},"GolangInc.",1000}
tom:=Employee{Human{"Tom",37,"222-444-XXX"},"ThingsLtd.",5000}
//定义Men类型的变量i
variMen
//i能存储Student
i=mike
fmt.Println("ThisisMike,aStudent:")
i.SayHi()
i.Sing("Novemberrain")
//i也能存储Employee
i=tom
fmt.Println("Thisistom,anEmployee:")
i.SayHi()
i.Sing("Borntobewild")
//定义了sliceMen
fmt.Println("Let'suseasliceofMenandseewhathappens")
x:=make([]Men,3)
//这三个都是不同类型的元素,但是他们实现了interface同一个接口
x[0],x[1],x[2]=paul,sam,mike
for_,value:=rangex{
value.SayHi()
}
}
空接口
空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
//定义a为空接口
varainterface{}
variint=5
s:="Helloworld"
//a可以存储任意类型的数值
a=i
a=s
interface的变量里面可以存储任意类型的数值(该类型实现了interface),那么我们怎么反向知道这个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:switch测试、Comma-ok断言。
switch测试如下:
typeElementinterface{}
typeList[]Element
typePersonstruct{
namestring
ageint
}
//打印
func(pPerson)String()string{
return"(name:"+p.name+"-age:"+strconv.Itoa(p.age)+"years)"
}
funcmain(){
list:=make(List,3)
list[0]=1//anint
list[1]="Hello"//astring
list[2]=Person{"Dennis",70}
forindex,element:=rangelist{
switchvalue:=element.(type){
caseint:
fmt.Printf("list[%d]isanintanditsvalueis%d\n",index,value)
casestring:
fmt.Printf("list[%d]isastringanditsvalueis%s\n",index,value)
casePerson:
fmt.Printf("list[%d]isaPersonanditsvalueis%s\n",index,value)
default:
fmt.Println("list[%d]isofadifferenttype",index)
}
}
}
如果使用Comma-ok断言的话:
funcmain(){
list:=make(List,3)
list[0]=1//anint
list[1]="Hello"//astring
list[2]=Person{"Dennis",70}
forindex,element:=rangelist{
ifvalue,ok:=element.(int);ok{
fmt.Printf("list[%d]isanintanditsvalueis%d\n",index,value)
}elseifvalue,ok:=element.(string);ok{
fmt.Printf("list[%d]isastringanditsvalueis%s\n",index,value)
}elseifvalue,ok:=element.(Person);ok{
fmt.Printf("list[%d]isaPersonanditsvalueis%s\n",index,value)
}else{
fmt.Printf("list[%d]isofadifferenttype\n",index)
}
}
}
嵌入接口
正如struct类型可以包含一个匿名字段,interface也可以嵌套另外一个接口。
如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。
参考文档:
http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/
http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/
本文内容总结:GoLang之方法与接口,方法,匿名组合,值语义和引用语义,接口,空接口,嵌入接口,
原文链接:https://www.cnblogs.com/chenny7/p/4497969.html