一、什麼是接口?
編程
在面向對象的語言中,接口是用來限制實現類行爲的。怎麼理解這句話呢?設計模式
定義一個Person接口,我只會站在個人角度上考慮問題,好比Person(人),天然想到會吃飯、睡覺等:數組
interface Person編程語言 {ide // 人會吃飯函數 void eat();測試 // 人會睡覺ui void sleep();spa }設計 |
我是站在接口角度上考慮接口如何定義,此時不會過多考慮實現類的行爲。
這很正常,由於我不能肯定誰會使用個人接口,有一天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語言
二、GO語言的接口呢?
GO語言有接口類型(interface{}),它與面向對象的接口含義不一樣,GO語言的接口類型與數組(array)、切片(slice)、集合(map)、結構體(struct)是同等地位的。怎麼理解這句話呢?
咱們前面已知道:
var num int // 定義了一個int型變量num |
同理:
var any interface{} // 定義了一個接口類型變量any |
從這個角度上看,GO的interface{}與面向對象的接口是不同吧。 更加不同的是,interface{}是一個任意類型,或者說是萬能類型。
三、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]) |
四、類型判斷
上例直觀上來看,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") } |
因此上面的例子能夠進一步修改以下:
// 定義一個長度爲3的Any類型數組,求數組元素之和,即anyArr[0]+anyArr[1]+anyArr[2] var anyArr [3]interface{} 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) |
五、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)會發現並無拋出錯誤!
從結果能夠看出徹底能夠這樣定義一個類型爲interface的變量watch,而且還能夠爲watch增長相應的方法;但與struct不一樣的是:struct裏面的成員是變量,而interface裏面的成員是函數,即咱們可使用interface定義接口。
六、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,這樣能行的通嗎?
把「嗎」字去掉,請看結果:
神奇吧 :)
以上是GO語言的接口實現。
(2)Java語言的接口實現
用面向對象的編程語言來解釋:
在面向對象的編程語言中,好比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中實現,那麼這種父子關係已創建,這種接口被稱爲「非侵入式接口」
七、引伸
上面例子中,爲iWatch定義了三個方法,如今咱們修改一下這三個方法:
// 爲*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() } |
這至關於並非爲iWatch類型增長了三個方法,而是爲*iWatch類型增長了三個方法,那麼調用時也須要相應修改:
func main() { var w watch var t iWatch w = &t fmt.Println("Current Hour:", w.getHour(), ", Minute:", w.getMinute(), ", Second:", w.getSecond()) } |
好了,關於GO語言的接口類型就聊到這裏,請記住這麼三句話:
interface是類型
interface類型的變量能夠賦值
任何實現了interface類型的具體類型變量,均可以賦值給interface類型的變量