Go接口interface
本文内容纲要:
-接口是什么?
-interface类型
-空接口(interface{})
-interface函数参数
-interface变量存储的类型
-类型断言
-嵌入interface
目录
-
接口是什么?
-
interface类型
-
空接口(interface{})
-
interface函数参数
-
interface变量存储的类型
- 类型断言
-
嵌入interface
接口是什么?
Go语言不是一种“传统”的面向对象编程语言:它里面没有类和继承的概念。
但是Go语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。接口提供了一种方式来说明对象的行为:如果谁能搞定这件事,它就可以用在这儿。
简单的说,interface是一组method的组合,我们通过interface来定义对象的一组行为,但是这些method接口不需要去实现,并且interface不能包含任何变量。到某个自定义类型(比如结构体Human)要使用的时候,再根据具体情况把这些方法写出来(实现这个方法)。
接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
通过如下格式定义接口:
type接口名interface{
Method1(param_list)return_type
Method2(param_list)return_type
...
}
示例:
//定义一个人类接口
typeHumaninterface{
Run()//奔跑
Laugh()//笑
Speak(wordsstring)//说话
}
interface类型
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了该接口。
示例:
packagemain
import(
"fmt"
)
//定义三个接口
typeMeninterface{
SayHi()
Sing(lyricsstring)
Guzzle(beerSteinstring)
}
typeYoungChapinterface{
SayHi()
Sing(songstring)
BorrowMoney(amountfloat32)
}
typeElderlyGentinterface{
SayHi()
Sing(songstring)
SpendSalary(amountfloat32)
}
//人类
typeHumanstruct{
namestring
ageint
phonestring
}
func(h*Human)SayHi(){
fmt.Printf("Hi,Iam%syoucancallmeon%s\n",h.name,h.phone)
}
func(h*Human)Sing(lyricsstring){
fmt.Println("Lala,lalala,lalalala...",lyrics)
}
func(h*Human)Guzzle(beerSteinstring){
fmt.Println("GuzzleGuzzleGuzzle...",beerStein)
}
//学生
typeStudentstruct{
Human
schoolstring
loanfloat32
}
func(s*Student)BorrowMoney(amountfloat32){
s.loan+=amount
}
//雇员
typeEmployeestruct{
Human
companystring
moneyfloat32
}
func(e*Employee)SayHi(){
fmt.Printf("Hi,Iam%s,Iworkat%s,Callmeon%s",e.name,e.company,e.phone)
}
func(e*Employee)SpendSalary(amountfloat32){
e.money-=amount
}
funcmain(){
vartmpMen=&Student{Human{"itbsl",23,"176xxxx3456"},"北京邮电大学",34}
fmt.Println(tmp.(*Student).name)
}
输出结果:
Hi,Iamitbslyoucancallmeon176xxxx3456
通过上面的代码我们可以看到,interface可以被任意的对象实现。我们看到上面的Meninterface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YoungChap两个interface。
注:上面实现接口类型的都是各个结构体的指针类型,所以接口变量存储的也只能是各个结构体类型的指针类型。如果实现接口的是各个结构体的值类型,那么接口变量能存储的就是指类型。
只要某个对象实现某个接口,那么该接口类型的变量可以用来存储该对象。上面我们把Student实例赋值给了一个Men类型的接口变量。
不仅仅是结构体类型,只要是自定义数据类型,就可以实现接口
typTestinterface{
Say()
}
typeintegerint
func(iinteger)Say(){
fmt.Println("integerSayi=",i)
}
variinteger=10
varbTest=i
i.Say()
空接口(interface{})
空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
示例:
//定义a为空接口
varainterface{}
variint=5
s:="Helloworld"
//a可以存储任意类型的数值
a=i
a=s
一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
interface函数参数
interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。
举个例子:fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:
typeStringerinterface{
String()string
}
也就是说,任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试
packagemain
import(
"fmt"
"strconv"
)
typeHumanstruct{
namestring
ageint
phonestring
}
//通过这个方法Human实现了fmt.Stringer
func(hHuman)String()string{
return"❰"+h.name+"-"+strconv.Itoa(h.age)+"years-✆"+h.phone+"❱"
}
funcmain(){
Bob:=Human{"Bob",39,"000-7777-XXX"}
fmt.Println("ThisHumanis:",Bob)
}
现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。
//实现同样的功能
fmt.Println("Thebiggestoneis",boxes.BiggestsColor().String())
fmt.Println("Thebiggestoneis",boxes.BiggestsColor())
注:实现了error接口的对象(即实现了Error()string的对象),使用fmt输出时,会调用Error()方法,因此不必再定义String()方法了。
interface变量存储的类型
我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?
类型断言
类型断言的语法就是用一个接口类型变量采用接口变量.(具体类型)
的方式进行转换
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言,具体的如下:
packagemain
import"fmt"
funcmain(){
varxinterface{}
varflofloat32=6.66
x=flo
y:=x.(float32)
fmt.Printf("y的类型是%T值是%v",y,y)
}
//输出结果如下
//y的类型是float32值是6.66
在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的空接口指向的就是断言的类型.
如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic,代码如下
packagemain
import"fmt"
funcmain(){
varxinterface{}
varflofloat32=6.66
x=flo
ify,ok:=x.(float64);ok{
fmt.Println("convertsuccess")
fmt.Printf("y的类型是%T值是%v",y,y)
}else{
fmt.Println("convertfail")
}
fmt.Println("继续执行...")
}
我们之前常用的typ-switch就是采用的类型断言
switch语句还可以被用于type-switch来判断某个interface变量中实际存储的变量类型。
packagemain
import"fmt"
funcmain(){
varxinterface{}
switchi:=x.(type){
casenil:
fmt.Printf("x的类型:%T",i)
caseint:
fmt.Printf("x是int型")
casefloat64:
fmt.Printf("x是float64型")
casefunc(int)float64:
fmt.Printf("x是func(int)型")
casebool,string:
fmt.Printf("x是bool或string型")
default:
fmt.Printf("未知型")
}
}
嵌入interface
Go里面真正吸引人的是它内置的逻辑语法,就像我们在学习Struct时学习的匿名字段,多么的优雅啊,那么相同的逻辑引入到interface里面,那不是更加完美了。如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。
我们可以看到源码包container/heap里面有这样的一个定义
typeInterfaceinterface{
sort.Interface//嵌入字段sort.Interface
Push(xinterface{})//aPushmethodtopushelementsintotheheap
Pop()interface{}//aPopelementsthatpopselementsfromtheheap
}
我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法:
typeInterfaceinterface{
//Lenisthenumberofelementsinthecollection.
Len()int
//Lessreturnswhethertheelementwithindexishouldsort
//beforetheelementwithindexj.
Less(i,jint)bool
//Swapswapstheelementswithindexesiandj.
Swap(i,jint)
}
另一个例子就是io包下面的io.ReadWriter,它包含了io包下面的Reader和Writer两个interface:
//io.ReadWriter
typeReadWriterinterface{
Reader
Writer
}
本文内容总结:接口是什么?,interface类型,空接口(interface{}),interface函数参数,interface变量存储的类型,类型断言,嵌入interface,
原文链接:https://www.cnblogs.com/itbsl/p/10483140.html