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

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 = &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,如需轉載請自行聯系原作者