【Go語言】物件導向擴充——介面

來源:互聯網
上載者:User

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)

        }

    }

}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.