伯樂線上注:本文來自Jiameng Hu(@philhu) 的翻譯投稿(英文原文)。
Interfaces 介面
在軟體工程中,由一份“契約”規定來自不同開發小組的軟體之間如何相互作用是非常常見的。每個小組都可以在不知道其他小組程式碼的前提下獨立開發自己的程式碼。Java中的interface就是這樣的一份“契約”
舉個例子,假設在未來社會有一種智慧汽車,它可以在自動運載旅客而不需要人工操作。汽車生產商開發了軟體(當然是用Java)來控制這種汽車停止,發動,加速,左轉等等。電子導航儀器生產商負責開發接受GPS位置資料和交通狀況無線傳輸的電腦系統,並且應用這些資訊來駕駛汽車。
汽車生產商必須公佈工業標準interface,該interface需詳細解釋哪些methods可以用於控制汽車運動(該標準適用於任何汽車,任何生產商)。導航系統生產商就可以應用這個interface所介紹的各種methods來控制汽車。任何一個工業廠商都不需瞭解其他廠商是如何實現他們的軟體的。事實上,只要大家都嚴格遵守所公佈的interface,每個廠商對其自己的軟體都有高度所有權,並且保有隨時修改的權利。
在java中的interface
在java程式語言裡,一個interface是引用型別(reference),它與class相似,因此只能包含常量(constants),方法簽名(method signatures)和巢狀型別(nested types)。Interface不得含有方法的具體程式碼(method body)。 Interface 不可被例項化(instantiated),只能被其它class實現(implemented)或者被其它interface繼承。
定義一個interface與建立一個新類類似:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public interface OperateCar { // constant declarations, if any // method signatures // An enum with values RIGHT, LEFT int turn(Direction direction, double radius, double startSpeed, double endSpeed); int changeLanes(Direction direction, double startSpeed, double endSpeed); int signalTurn(Direction direction, boolean signalOn); int getRadarFront(double distanceToCar, double speedOfCar); int getRadarRear(double distanceToCar, double speedOfCar); …… // more method signatures } |
如想使用上面這個interface,你需要寫一個class來實現它。當一個可被例項化的class實現某個介面時,它需要提供所有該interface所宣告的所有方法(methods)的具體程式碼。
在上面的自動化汽車的例子中,汽車生產商即是介面實現者。由雪佛蘭公司的實現方法當然不同於本田公司的方法,但是它們都遵循同一個介面。 導航系統生產商是這個介面的使用者,他們的系統將根據汽車方位的GPS資料,數字化地圖和交通情況來駕駛汽車。因此,這個導航系統將會涉及以下的方法(methods): 轉彎,換車道,剎車,加速等等。
API 介面
自動化汽車的例子展示了interface在工業標準應用程式介面(API, Application Programming Interface)中的應用。在商業軟體中,API也很常見。 通常來說, 一個公司發售的軟體包中會含有其他公司希望應用在自己的產品中的複雜方法(methods)。比如一個包含了數字圖形處理方法的軟體包就可以出售給開發終端客戶影象軟體的公司。購買後,該公司就可以應用interface所定義的方法。當影象處理公司向所有客戶公開它的API的同時,這些API的實現方法是高度保密的。事實上,只要保留住原始的interface不被改變,這些API的實現方法很可能在將來被重寫。
Interfaces 和多重繼承
在java程式語言裡,interface還有另外一個重要作用。 儘管Interface是與類一起使用的,但它並不是類的層次結構的一部分。java程式語言不支援多重繼承,但是interface提供了替代方案。
在java中,一個類只能繼承於單一的類,但是它可以實現多個介面。因此,物件可以有多重型別:屬於它自身類的型別,和屬於它所繼承的所有介面的型別。這意味著,如果宣告一個變數是某個介面型別,這個變數可以指代任何實現該介面的類的例項。這部分會在“使用介面型別”中詳細討論。
定義一個interface
一個介面的定義是由 修飾詞(modifiers),關鍵詞 interface, 介面名稱,由逗號分隔開的父介面(parent interfaces),和介面實體(interface body)。
例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public interface GroupedInterface extends Interface1, Interface2, Interface3 { // constant declarations // base of natural logarithms double E = 2.718282; // method signatures void doSomething (int i, double x); int doSomethingElse(String s); } |
Public規定了這個介面可以被任何包中的任何類所使用。如果你宣告這個介面是public的,它只能被同一個包裡的類所使用。
一個介面可以繼承其它介面,就像一個類能後繼承其它類一樣。但是類只能繼承一個父類,而介面卻可以繼承任何數目的介面。
介面實體(interface body)
介面實體中含有它所包含的所有方法的宣告。每個宣告都以引號為結束,因為介面不用實現它所宣告的方法。介面中所有的方法都預設是public的,因此修飾詞public可以被省略。
介面還可以宣告常量。同樣的,常量的修飾詞public, static和final可以被省略。
介面的實現
為了宣告某個類實現了某個介面,你需要在類的宣告中使用implements。你的類可以實現多個介面,所以implements關鍵詞後面可以跟隨多個由逗號分隔的介面名稱。為了方便,implements關鍵詞多跟在extends關鍵詞的後面。
一個介面例項—Relatable
Relatable是一個用來比較兩個物件大小的介面。
1 2 3 4 5 6 7 8 9 |
public interface Relatable { // this (object calling isLargerThan) // and other must be instances of // the same class returns 1, 0, -1 // if this is greater // than, equal // to, or less than other public int isLargerThan(Relatable other); } |
如果你想比較兩個相似的物件的大小,不管該物件屬於什麼類,這個類需要實現Relatable介面。
只要有辦法可以比較物件的相對大小,任何類都可以實現Relatable介面。對字串來說,可以比較字元數;對書來說,可以比較頁數;對學生來說,可以比較體重。對平面幾何物件來說,比較面積是很好的選擇;對三維物件來說,就需要比較體積了。所有以上的類都能實現int isLargerThan()方法。
如果你知道某個類實現了Relatable介面,你可以比較從這個類例項化的物件了。
Relatable介面的實現
下面是一個三角形類,它實現了Relatable介面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public class RectanglePlus implements Relatable { public int width = 0; public int height = 0; public Point origin; // four constructors public RectanglePlus() { origin = new Point(0, 0); } public RectanglePlus(Point p) { origin = p; } public RectanglePlus(int w, int h) { origin = new Point(0, 0); width = w; height = h; } public RectanglePlus(Point p, int w, int h) { origin = p; width = w; height = h; } // a method for moving the rectangle public void move(int x, int y) { origin.x = x; origin.y = y; } // a method for computing // the area of the rectangle public int getArea() { return width * height; } // a method required to implement // the Relatable interface public int isLargerThan(Relatable other) { RectanglePlus otherRect = (RectanglePlus)other; if (this.getArea() < otherRect.getArea()) return -1; else if (this.getArea() > otherRect.getArea()) return 1; else return 0; } } |
使用介面型別
在你定義一個新的介面時,你其實在定義一個新的引用型別。在你能使用資料型別名稱的地方,都可以使用介面名稱。如果你定義了一個型別為介面的引用變數,該變數能指向的物件所在的類必須實現了該介面。
下例是一個在一對物件中返回較大物件的方法:
1 2 3 4 5 6 7 8 |
public Object findLargest(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ((obj1).isLargerThan(obj2) > 0) return object1; else return object2; } |
通過把資料型別object1轉換成Relatable,物件obj1可以呼叫isLargerThan方法。
同理,只要是實現了Relatable的類,也可以使用下面的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public Object findSmallest(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ((obj1).isLargerThan(obj2) < 0) return object1; else return object2; } public boolean isEqual(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ( (obj1).isLargerThan(obj2) == 0) return true; else return false; } |
這些方法適用於任何“Relatable”的類,而無關它們的繼承關係。實現了Relatable的類,它們既屬於自身(或者父類)的型別,也屬於Relatable型別。這使得它們具有了多重繼承的優點,因為它們可以同時具備父類和介面的行為。
重寫介面
假設你開發了一個介面名為DoIt:
1 2 3 4 |
public interface DoIt { void doSomething(int i, double x); int doSomethingElse(String s); } |
然後,你想加入一個新的方法在這個介面裡,因此程式碼變成:
1 2 3 4 5 |
public interface DoIt { void doSomething(int i, double x); int doSomethingElse(String s); boolean didItWork(int i, double x, String s); } |
如果你這麼修改了,所有實現了舊的DoIt介面的類都會出錯,因為它們不再正確的實現這個介面。所有使用這個介面的程式設計師會嚴重抗議你的修改。
你需要預估你的介面使用者的需求,並從開始就完善的設計好這個介面。但是這常常是無法做到的。另一個解決方法就是再寫一個介面。例如,你可以寫一個DoItPlus的介面繼承原有的介面。
1 2 3 |
public interface DoItPlus extends DoIt { boolean didItWork(int i, double x, String s); } |
現在你的使用者可以選擇繼續使用舊介面DoIt,或是升級的新介面DoItPlus。
總結
介面就是兩個物件間的溝通協議。
一個介面的宣告包含一些方法的簽名(signatures),但不需要實現它們;也可能含有一些常量。
實現某介面的類必須實現該介面所宣告的所有的方法。
在任何使用型別名稱的地方都可以使用介面的名字。
譯文連結:http://blog.jobbole.com/32544/
【如需轉載,請在正文中標註並保留原文連結、譯文連結和譯者等資訊,謝謝合作!】