天天看點

【Go語言】【14】GO語言的接口類型

1、什麼是接口?

在面向對象的語言中,接口是用來限制實作類行為的。怎麼了解這句話呢?

定義一個Person接口,我隻會站在我的角度上考慮問題,比如Person(人),自然想到會吃飯、睡覺等:

interface Person

{

      // 人會吃飯

      void eat();

      // 人會睡覺

      void sleep();

}

我是站在接口角度上考慮接口如何定義,此時不會過多考慮實作類的行為。

這很正常,因為我不能确定誰會使用我的接口,有一天SuperMan說:“我要用你定義的接口”,那SuperMan必須用implements實作Person接口的行為:

// SuperMan實作Person接口

public class SuperMan implements Person

      // 超人會吃飯

       public void eat()

       {

              System.out.println("super man can eat.");

       }

       // 超人會睡覺

       public void sleep()

              System.out.println("super man can sleep.");

等到SuperMan實作完了之後,他對我說:“作為超人,我是會飛的哦~”

這時作為Person定義者的我,隻有兩個選擇:

對SuperMan說:“飛是你自己的行為,我不幫你定義這種行為!”。可是經過若幹萬年之後人類進化了怎麼辦?

對SuperMan說:“好吧,我幫你定義這種行為,可是一旦Person有了fly,你也必須實作fly”

其實無論上面哪種結果,都相當于接口把實作類綁架了。

【備注】:悄悄地告訴你,上面的代碼是Java語言                                                                                                      

2、GO語言的接口呢?

GO語言有接口類型(interface{}),它與面向對象的接口含義不同,GO語言的接口類型與數組(array)、切片(slice)、集合(map)、結構體(struct)是同等地位的。怎麼了解這句話呢?

我們前面已知道:

var num int  // 定義了一個int型變量num

同理:

var any interface{} // 定義了一個接口類型變量any

【Go語言】【14】GO語言的接口類型

3、GO語言的任意類型

也就是說定義一個變量為interface{}類型,可以把任意的值賦給這個變量,例如:

var v1 interface{} = 250       // 把int值賦給interface{}

var v2 interface{} = "eagle" // 把string值賦給interface{}

var v3 interface{} = &v1      // 把v1的位址賦給interface{}

當然函數的入參類型也可以是interface{},這樣函數就可以接受任意類型的參數,例如GO語言标準庫fmt中的函數Println()

func Println(args ...interface{}){

        // 略

任意類型看起來很爽,可以把任意的值都賦給interface{}類型變量,就像JDK1.4時的Vector,那時候Java還沒有泛型的概念,任意值都可以向Vector裡面放,但問題也接踵而至:

// 定義一個長度為3的Any類型數組,求數組元素之和,即anyArr[0]+anyArr[1]+anyArr[2]

var anyArr [3]interface{}

anyArr[0] = "eagle"    // anyArr[0]指派為字元串

anyArr[1] = 20           // anyArr[1]指派為int

anyArr[2] = 75.3        // anyArr[2]指派為float64

// 此時若求和,會有什麼結果呢?

fmt.Println(anyArr[0] + anyArr[1] + anyArr[2])

4、類型判斷

上例直覺上來看,string不能和int直接相加,是以我們需要判斷元素類型,若元素類型是數字型的,我們就執行“+”操作;若元素類型是字元串型的,我們就跳過。這裡需要引入另外一個知識:switch-type

即:拿到一個interface{}之後,可以結合switch語句判斷變量的類型                                                                            

例如:

var v interface{} = 3

switch v.(type){

      case int:

              fmt.Println("3 is int")

      case string:

              fmt.Println("3 is string")

      default:

              fmt.Println("unkown type")

是以上面的例子可以進一步修改如下:

anyArr[0] = "eagle"

anyArr[1] = 20

anyArr[2] = 75.3

// 定義一個總和變量total

var total float64 = 0

// 周遊Any類型數組

for i := 0; i < len(anyArr); i++ {

        // 針對Any類型數組中的每個元素進行類型查詢

        switch vType := anyArr[i].(type) {

              case int:

                      total += float64(vType)

              case float64:

                      total += vType

              default:

                      // do nothing

        }

// 列印Any類型數組中數字之和

fmt.Println(total)

5、interface類型與struct類型

 從上面看interface類型很簡單嘛,或許吧。

我們再回顧一下struct類型,struct類型是一個結構體,裡面可以定義成員,它類似面向對象的一個類,類裡面可以定義成員變量,比如:

// 定義一個person結構體,裡面有姓名、年齡、身高、體重成員

type person struct{

        name                 string

        age                    int

        height, weight   float64

那麼interface類型是否也可以這樣定義呢?如下:

/**

 * 定義一個手表接口,通過手表接口我們可以知道小時、分鐘和秒

 */

type watch interface {

        getHour() int

        getMinute() int

        getSecond() int

通過編譯(go build myIf.go)會發現并沒有抛出錯誤!

<a href="http://s3.51cto.com/wyfs02/M00/6F/B1/wKiom1WlKLnStanZAADiDZWEgn0265.jpg" target="_blank"></a>

從結果可以看出完全可以這樣定義一個類型為interface的變量watch,并且還可以為watch增加相應的方法;但與struct不同的是:struct裡面的成員是變量,而interface裡面的成員是函數,即我們可以使用interface定義接口。

6、interface定義接口示例

(1)GO語言接口實作

       周圍的不少朋友現在都有一款iWatch智能手表,一般都用來運動時監控心率,這也意味着iWatch不僅能看時間這麼簡單。下面我們定義一個iWatch類型:

type iWatch int    // 定義一個iWatch類型,它實際上就是int型;相當于為int型取一個别名iWatch

接下來為iWatch類型增加三個方法,分别為getHour()、getMinute()、getSecond()

// 為iWatch增加getHour()方法

func (w iWatch) getHour() int {

        return time.Now().Hour()

// 為iWatch增加getMinute()方法

func (w iWatch) getMinute() int {

        return time.Now().Minute()

// 為iWatch增加getSecond()方法

func (w iWatch) getSecond() int {

        return time.Now().Second()

下面是GO語言的精彩内容,請各位看客睜大眼睛:

func main() {

        var w watch  // 定義類型為watch的變量w

        var t iWatch  // 定義類型為iWatch的變量t

        w = t             // 把類型為watch的變量w指派給類型為iWatch的變量t,這樣能行的通嗎?

        fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond())

在這個測試代碼中:

var w watch

相當于定義了一個接口變量

var t iWatch

相當于定義了一個iWatch對象

w = t

直接把對象t 賦給了接口變量w,但沒有像其它面向對象語言那樣,讓iWatch implements watch,這樣能行的通嗎?

把“嗎”字去掉,請看結果:

<a href="http://s3.51cto.com/wyfs02/M00/6F/B8/wKioL1WmVRujSREcAAB1baVrR5Y574.jpg" target="_blank"></a>

神奇吧 :)

以上是GO語言的接口實作。

(2)Java語言的接口實作

用面向對象的程式設計語言來解釋:

<a href="http://s3.51cto.com/wyfs02/M01/6F/BA/wKiom1WmVGnjDIhoAAAaPrHbUKw134.jpg" target="_blank"></a>

在面向對象的程式設計語言中,比如Son是一個實作類,Father是一個接口,Son要實作Father接口,必須使用implements顯式地聲明,同時在Son中實作Father裡面定義的方法,比如:

interface Father{

         getHour();

class Son implements Father{

         // 實作父接口Father定義的方法getHour()

         public int getHour(){

                 return 20;

         }      

一旦接口Father增加一個方法getSecond(),那麼實作該接口的所有孩兒都必須實作getSecond()方法。在使用時:

Father father = new Son();

即孩兒對象可以指派給Father接口

【備注】:若對上面面向對象程式設計語言不熟悉的話,建議看一下設計模式相關的書籍

(3)侵入式接口和非侵入式接口

像上面(2)中的Java就是侵入式接口。

GO語言中,像上面(1)所示,盡管定義了接口watch,但實作類iWatch并沒有顯示地聲明實作該接口,隻是watch中的方法都已在iWatch中實作,那麼這種父子關系已建立,這種接口被稱為“非侵入式接口”

7、引申

上面例子中,為iWatch定義了三個方法,現在我們修改一下這三個方法:

// 為*iWatch增加getHour()方法

func (w *iWatch) getHour() int {

// 為*iWatch增加getMinute()方法

func (w *iWatch) getMinute() int {

// 為*iWatch增加getSecond()方法

func (w *iWatch) getSecond() int {

這相當于并不是為iWatch類型增加了三個方法,而是為*iWatch類型增加了三個方法,那麼調用時也需要相應修改:

        var w watch

        var t iWatch

        w = &amp;t        

好了,關于GO語言的接口類型就聊到這裡,請記住這麼三句話:

interface是類型

interface類型的變量可以指派

任何實作了interface類型的具體類型變量,都可以指派給interface類型的變量

<a href="http://down.51cto.com/data/2365912" target="_blank">附件:http://down.51cto.com/data/2365912</a>

     本文轉自qingkechina 51CTO部落格,原文連結:http://blog.51cto.com/qingkechina/1675115,如需轉載請自行聯系原作者