From:https://www.cnblogs.com/Mike-zh/p/3787679.html
簡單地說 Interface是一組Method的組合,可以通過Interface來定義對象的一組行為。
如果某個對象實現了某個介面的所有方法,就表示它實現了該借口,無需顯式地在該類型上添加介面說明。
Interface是一個方法的集合,它裡面沒有其他類型變數,而且Method只用定義原型 不用實現
①介面定義
1.命名時習慣以"er"結尾,如Printer Reader Writer
2.一個Interface的Method不宜過多,一般0~3個
3.一個Interface可以被任意的對象事項;相應地,一個對象也可以實現多個Interface
樣本:
type Peoplestruct{
Name string}
type Student struct{
People
School string}
type Teacher struct{
People
Department string}
func (p People) SayHi(){}
func (s Student) SayHi(){}
func (t Teacher) SayHi(){}
func (s Student) Study(){}//根據struct的方法提取介面 從而使struct自動實現了該介面type Speakerinterface{
SayHi()
}
type Learner interface{
SayHi()
Study()
}
上面的例子中,Speaker介面被對象People,Teacher,Student實現;而Student同時實現了介面Speaker和Learner。
介面組合:
type SpeakLearnerinterface {
Speaker
Learner
}//組合後使得SpeakLearner具有Speaker和Learner的功能
空介面:
任何類型都實現了空介面,相當於Java中的Object類
func test(ainterface{}){}//該方法可以接受任意類型(int rune float32 struct...)的參數
②介面執行機制和介面賦值
首先介紹一種Go語言帶接收者(Receiver)的函數機制(下面的兩種情況執行結果一樣,涉及到struct成員值改變時仍然一樣)
情況1:
package main
import (
"fmt")
type People struct {
Name string}
func (p People) SayHi(){ //此處的Receiver是strcutfmt.Println("hello, this is", p.Name)
}
func (p *People) Study(){//此處的Receiver是****structfmt.Printf("%s is studying\n", p.Name)
}
type SpeakLearner interface {
SayHi()
Study()
}
func main() {
people := People{"zhangsan"}//這裡的people為People類型 people.SayHi()
people.Study()
}
情況2:
func main() {
people := &People{"zhangsan"}//這裡的people為**People類型,即指標 people.SayHi()
people.Study()
}
通過上面的例子可以看出Receiver為People和*People的函數均可被People或者*People兩種類型調用,接下來借可能有在調用過程中People與*People之間的轉換問題
看下面的例子:
package main
import (
"fmt")
type Example struct{
Integer1 int Integer2 int}
func (e Example) Assign(num1 int, num2int) {
e.Integer1, e.Integer2 = num1, num2
}
func (e *Example) Add(num1int, num2int) {
e.Integer1 +=num1
e.Integer2 +=num2
}
func main(){
vare1 Example = Example{3,4}
e1.Assign(1,1)
fmt.Println(e1)
e1.Add(1,1)
fmt.Println(e1)
vare2 *Example = &Example{3,4}
e2.Assign(1,1)
fmt.Println(e2)
e2.Add(1,1)
fmt.Println(e2)
}
以上程式的執行結果為:
{3,4}
{4,5}&{3,4}&{4,5}
可以看出實際執行的過程按函數定義前的Receiver類型執行。
對於介面的執行機制:
1.T僅擁有屬於T類型的方法集,而*T則同時擁有(T+*T)方法集
2.基於T實現方法,表示同時實現了interface和interface(*T)介面
3.基於*T實現方法,那就只能是對interface(*T)實現介面
type Integerintfunc (a Integer) Less(b Integer) bool {
returna < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
相應地,我們定義介面LessAdder,如下:
type LessAdder interface {
Less(b Integer) bool Add(b Integer)
}
現在有個問題:假設我們定義一個Integer類型的對象執行個體,怎麼將其賦值給LessAdder介面呢?
應該用下面的語句(1),還是語句(2)呢?vara Integer =1varb LessAdder = &a ... (1)varb LessAdder = a ... (2)
答案是應該用語句(1)。原因在於,Go語言可以根據下面的函數:
func (a Integer) Less(b Integer) bool
即自動產生一個新的Less()方法:
func (a *Integer) Less(b Integer)bool {
return(*a).Less(b)
}
這樣,類型*Integer就既存在Less()方法,也存在Add()方法,滿足LessAdder介面。
而從另一方面來說,根據
func (a *Integer) Add(b Integer)
這個函數無法自動產生以下這個成員方法:
func (a Integer) Add(b Integer) {
(&a).Add(b)
}
因為(&a).Add()改變的只是函數參數a,對外部實際要操作的對象並無影響,這不符合用
戶的預期。所以,Go語言不會自動為其產生該函數。
因此,類型Integer只存在Less()方法,缺少Add()方法,不滿足LessAdder介面,故此上面的語句(2)不能賦值。
介面賦值舉例:
package main
import(
"fmt")//定義對象People、Teacher和Studenttype Peoplestruct {
Name string}
type Teacher struct{
People
Department string}
type Student struct{
People
School string}//對象方法實現func (p People) SayHi() {
fmt.Printf("Hi, I'm %s. Nice to meet you!\n",p.Name)
}
func (t Teacher) SayHi(){
fmt.Printf("Hi, my name is %s. I'm working in %s .\n", t.Name, t.Department)
}
func (s Student) SayHi() {
fmt.Printf("Hi, my name is %s. I'm studying in %s.\n", s.Name, s.School)
}
func (s Student) Study() {
fmt.Printf("I'm learning Golang in %s.\n", s.School)
}//定義介面Speaker和Learnertype Speakerinterface{
SayHi()
}
type Learner interface{
SayHi()
Study()
}
func main() {
people := People{"張三"}
teacher := Teacher{People{"鄭智"},"Computer Science"}
student := Student{People{"李明"},"Yale University"}
varisSpeaker//定義Speaker介面類型的變數is= people//is能儲存Peopleis.SayHi()
is= teacher//is能儲存Teacheris.SayHi()
is= student
is.SayHi()//is能儲存Studentvar il Learner
il = student//Learner類型介面的變數能儲存Student il.Study()
}
執行結果為:
Hi, I'm 張三. Nice to meet you!Hi, my nameis鄭智. I'm working in Computer Science .Hi, my nameis李明. I'm studying in Yale University.I'm learning Golang in Yale University.
通過這個例子可以 看到(如同Java等語言)介面機制在多態和建立可擴充可重用的代碼時的重要作用
③匿名欄位和介面轉換
若果介面類型S內部嵌入了介面類型T(匿名),則介面匿名欄位方法集規則如下:
1.如果S嵌入匿名型別T,則S方法集包含T方法集。
2.如果S嵌入匿名型別*T,則S方法集包含*T方法集(包括Riceiver為T和*T的方法)。
3.如果S嵌入匿名型別T或*T,則*S方法集包含*T方法集(包括Riceiver為T和*T的方法)。(重要)
例如:
package main
import(
"fmt"
)
type People struct {
Name string}
type S1 struct{
People //S1類型嵌入匿名PeopleDepartmentstring}
type S2 struct{
*People//S2類型嵌入匿名*PeopleDepartmentstring}
func (p People) Say1() {
fmt.Printf("Hi, I'm %s. Say1111\n",p.Name)
}
func (p *People) Say2() {
fmt.Printf("Hi, I'm %s. Say2222\n",p.Name)
}
type Speaker interface{
Say1()
Say2()
}
func main() {
people := People{"張三"}
s1 := S1{People{"鄭智"},"Computer Science"}
s2 := S2{&People{"李明"},"Math"}
varis Speaker
is= &people//*People實現了Speaker介面is.Say1()
is.Say2()
//is = s1 //S1類型嵌入匿名People 不存在Say2()方法 因而未實現Speaker介面
//錯誤提示: cannot use s1 (type S1) as type Speaker in assignment:
//S1 does not implement Speaker (Say2 method has pointer receiver)is= s2//S2類型嵌入匿名*People 因而(p People) Say1()和(p *People) Say2()方法都有 實現了Speaker介面is.Say1()
is.Say2()
is= &s1//S1類型嵌入匿名People *S1 實現了Speaker介面is.Say1()
is.Say2()
is= &s2//S2類型嵌入匿名*People *S2 實現了Speaker介面is.Say1()
is.Say2()
}
執行結果為:
Hi, I'm 張三. Say1111Hi, I'm 張三. Say2222Hi, I'm 李明. Say1111Hi, I'm 李明. Say2222Hi, I'm 鄭智. Say1111Hi, I'm 鄭智. Say2222Hi, I'm 李明. Say1111Hi, I'm 李明. Say2222
從而證明了匿名欄位方法集的3條規則。
介面轉換類似於說是介面繼承規則 可認為是實現複雜介面(方法多)向簡單介面(方法少)轉換,其中簡單介面中的方法在複雜介面中均有聲明 。例如:
package main
import(
"fmt")
type People struct {
Name string}
type Student struct{
People
School string}
func (p People) GetPeopleInfo() {
fmt.Println(p)
}
func (s Student) GetStudentInfo() {
fmt.Println(s)
}
type PeopleInfo interface{
GetPeopleInfo()
}
type StudentInfo interface{
GetPeopleInfo()
GetStudentInfo()
}
func main() {
varisStudentInfo = Student{People{"李明"},"Yele University"}
is.GetStudentInfo()
is.GetPeopleInfo()
varip PeopleInfo =is ip.GetPeopleInfo()
///ip.GetStudentInfo() note:ip.GetStudentInfo undefined}
④介面類型推斷:Comma-ok斷言和Switch測試
利用介面類型推斷可以 反向知道介面類型變數裡面實際儲存的是哪一種類型的對象。
Go語言中,常用兩種方法可以進行介面類型推斷,即Comma-ok斷言和Switch測試
Comma-ok斷言使用格式如下
value,ok = element.(T)
用法樣本:
//利用Comma-ok斷言進行介面類型推斷package main
import(
"fmt")
type People struct{
Name string Age int}//定義空介面用於儲存任意類型資料類型type Objectinterface{}
func main() {
people := People{"張三",20}
objs := make([]Object,4)
objs[0], objs[1], objs[2], objs[3] =1,true,"Hello", people
forindex, element := range objs{
ifvalue, ok := element.(int); ok{
fmt.Printf("objs[%d]類型是int,value=%d\n", index, value)
}elseifvalue, ok := element.(bool); ok{
fmt.Printf("objs[%d]類型是bool,value=%v\n", index, value)
}elseifvalue, ok := element.(string); ok{
fmt.Printf("objs[%d]類型是string,value=%s\n", index, value)
}elseifvalue, ok := element.(People); ok{
fmt.Printf("objs[%d]類型是Peole,value=%v\n", index, value)
}else{
fmt.Printf("objs[%d]類型未知\n", index)
}
}
}
結果是這樣的:
objs[0]類型是int,value=1objs[1]類型是bool,value=trueobjs[2]類型是string,value=Hello
objs[3]類型是Peole,value={張三20}
使用Switch測試判斷介面類型,程式結構更加簡潔,樣本如下(只修改了樣本中的main函數):
func main() {
people := People{"張三",20}
objs := make([]Object,4)
objs[0], objs[1], objs[2], objs[3] =1,true,"Hello", people
forindex, element := range objs{
switchvalue := element.(type){
caseint:
fmt.Printf("objs[%d]類型是int,value=%d\n", index, value)
casebool:
fmt.Printf("objs[%d]類型是bool,value=%v\n", index, value)
casestring:
fmt.Printf("objs[%d]類型是string,value=%s\n", index, value)
case People:
fmt.Printf("objs[%d]類型是Peole,value=%v\n", index, value)
default:
fmt.Printf("objs[%d]類型未知\n", index)
}
}
}