Java語言提供了一種介面(interface)機制。這種介面機制使Java的物件導向編程變得更加靈活。我們可以用介面來定義一個類的表現形式,但介面不能包含任何實現。在《Thinking in Java》一書中,作者對介面有這樣的描述:“介面(interface)比抽象(abstract)的概念更進了一步。你可以把一個介面看成是一個純的抽象類別。”我認為作者對介面的這一解釋再準確不過了。
理解並用好介面機制將協助我們更好的掌握Java這種物件導向的程式設計語言。下面我們來討論一下介面的使用規則以及相關的應用。
一、介面的定義及實現
定義介面和定義類相似,只是要把 class關鍵字換為 interface。定義方法時只需要方法名,傳回型別和參數列表,不能有方法體。介面中可以定義欄位,這些欄位都被暗指為 static 和 final,因此應該根據需要先定好這些欄位的值。例如:
public interface Flyable { void fly(); }public interface Talkable { void talk(); } public interface Message { int MAX_SIZE = 4096; String getMessage(); } |
上面定義的幾個介面中,Flyable 和 Talkable 只定義了一個方法,而 Message 裡除了方法外還有一個欄位 MAX_SIZE。可以看出這些介面只定義了類的表現形式,而不包含任何實現,所以不能直接使用。要使用這些介面就需要有相應的類去實現它們。實現介面時應該先在類名後用 implements 關鍵字申明將要實現的介面,如果要實現多個介面,應該用逗號將它們隔開,然後一一實現這些介面中定義的方法。如下面的例子:
public class Parrot implements Flyable, Talkable { public void fly() { System.out.println("Flying like a parrot…"); } public void talk() { System.out.println("Hello! I am a parrot!"); } } public class TextMessage implements Message { String message; public void setMessage(String msg) { message = msg; if (message.length() > MAX_SIZE) message = message.substring(0, MAX_SIZE); } public String getMessage() { return message; } } |
在 Parrot(鸚鵡)例子中,我們用介面 Flyable 來表示飛行能力,Talkable 表示說話能力,但它們並不包含具體實現。而 Parrot 同時具有這兩種能力,所以我們為 Parrot 類同時實現了 Flyable 和 Talkable 這兩個介面。同樣我們還可以定義一個Swallow(燕子)類,但燕子只有飛行能力,所以我們只需要為 Swallow 實現 Flyable 就行了。因為它們各自的的飛行方法有所不同,所以它們有各自關于飛行的具體實現。
另外,正因為一個類可以同時實現多個介面,使得Java的物件導向特性變得非常靈活。運用這種特性,我們可以實作類別似C++語言中多繼承那樣的特性,甚至更靈活的一些特性。下面我們來討論一下介面在實際中的應用。
二、用介面來定義一些全域變數
因為介面內的欄位都是static和final的,所以我們可以很方便的利用這一點來建立一些常量。例如:
public interface Constants { String ROOT = "/root"; int MAX_COUNT = 200; int MIN_COUNT = 100; } |
在使用時可以直接用Constants.ROOT這樣的形式來引用其中的常量。我們還可以用下面這種方法來建立初始值不確定的常量。
public interface RandomColor { int red = Math.random() * 255; int green = Math.random() * 255; int blue = Math.random() * 255; } |
其中red、green和blue的值會在第一次被訪問時建立,然後保持不變。
三、用介面來定義基本資料結構
在設計一套軟體系統的初期,我們可以用介面來對一些基本資料元素的特性來進行一些描述,再根據需要進行不同的實現。請大家看看下面這個例子:
public interface User { int getAge(); String getName(); String getPassword(); }public class XMLUser implements User { // 這裡用XML技術實現User介面中的方法 public int getAge() { ... } public String getName() { ... } public String getPassword() { ... } } public abstract class UserFactory { public static UserFactory getUserFactory() { return new XMLUserFactory(); } public User getUser(String name); public User getAdmin(); public User createUser(String name, String password, int age); public void addUser(User user); public void delUser(User user); } public class XMLUserFactory extends UserFactory { // 這裡用XML技術實現的UserFactory的抽象方法 } |
在這個例子中,我們定義了一個介面User和一個抽象類別UserFactory。然後我們用XML技術實現這兩個類。可以看出,我們只需要從用UserFactory的getUserFactory()就可以得到一個UserFactory的執行個體,而不用去考慮這個執行個體的具體實現方法。通過UserFactory的這個執行個體我們還可以直接得到User的執行個體,也不用去考具體的實現方法。
如果我們決定用JDBC技術來實現User和UserFactory,我們只需要按上面的形式實現JDBCUser和JDBCUserFactory就行了。然後把UserFactory中的getUserFactory方法修改一下就可以改變了它們的實現方法。而我們已經寫好的調用UserFactory和User的部分不需要做任何修改。
這是用介面來定義資料結構的一個簡單的例子,在實際應用中還有很多靈活的使用方法,大家需要在學習過程中不斷的去體會。
四、理解分布式應用的原理
目前有很多軟體項目都使用了分布式的技術。Java 有多種支援分布式應用的技術,早期用的比較多的有 RMI、CORBA 等技術,而現在 EJB 技術更為流行一些。但這些技術不管怎麼發展,其實都是以介面為基礎的。
以遠程方法調用 RMI(Remote Method Invocation)為例。在編寫 RMI 應用時,我們需要做兩件最基本的事,首先要定義一個介面,這個介面要繼承 java.rmi.Remote 介面,這個介面中應該包含你要從遠端調用的方法名。接下來就是寫一個類來實現這個介面中的方法。例如:
public interface Product extends java.rmi.Remote { String getName() throws java.rmi.RemoteException; }public class ProductImpl implements Product { String name; public ProductImpl(String n) { name = n; } public String getName() throws java.rmi.RemoteException { return name; } } |
在這個例子中,介面 Product 是放在用戶端的,而 ProductImpl 是放在伺服器端的,客戶在使用時只需要用指定的規則得到Product 的執行個體就行了,不用去考慮 Product 介面裡的方法是如何?的。在定義好這兩個類後,用Java開發包命令“rmic ProductImpl”就可以協助我們自動產生兩個類 ProductImpl_Skel 和 ProductImpl_Stub。這兩個類就包含了RMI調用的運作機制。有興趣的朋友可以把這兩個類反編譯後研究一下。你會發現其中 ProductImpl_Stub 實際上是介面 Product 的一個實作類別。RMI 機制就是用這個類來產生 Product 的執行個體供用戶端使用。另一個類 ProductImpl_Skel 則是在服務端響應 ProductImpl_Stub 的調用請求的類。而 RMI 最底層的通訊原理則是利用 ObjectInputStream 和 ObjetOutputStream 通過 Socket 將要調用的方法名及參數列表傳到伺服器端,伺服器端再通過特定的方法調用實作類別(在本例中是 ProductImpl)的對應方法,然後將結果通過 Socket 傳回用戶端就行了。由於 Skel 和 Stub 類是用工具產生的,所以就大大節省了開發的時間。另外,如果我們需要修改一些實現方法或錯誤,只需要對伺服器端的實作類別進行修改就可以了,也就是說這種分布式應用的大部分維護工作在伺服器端就可以完成。
現在越來越多的應用使用了 EJB 這種技術。EJB 是從 RMI 發展而來的一項技術,它比RMI定義得更加完善,可以獲得更好的物件導向的特性。但它的規則要比RMI複雜一些。但是不管它多複雜,它同樣是使用了介面來定義各種不同的 Bean,也同樣需要編寫相應的實作類別來完成具體的功能,最後還要通過 Socket 來進行通訊。EJB的運作機制本身有一定的複雜性,所以其應用的效率理所當然就會受到一定的影響。因此在選擇開發技術時應該根據應用的規模和特點仔細考慮,不一定流行的技術就一定能適應你的應用。如果你很好的掌握了物件導向的設計原則,你就可以自行設計。也許你可以根據自己應用的特點設計出更合適的分布式應用結構。
五、結論
除了上述的一些應用外,還有很多地方可以使用介面,比如在Java的事件機制中就常用到介面。另外,對於一些已經開發好的系統,在結構上進行較大的調整已經不太現實,這時可以通過定義一些介面並追加相應的實現來完成功能結構的擴充。
總之,學好介面可以協助我們更好的理解和運用物件導向的設計原則。使我們能設計出更好的軟體系統。由於本人水平的限制,如有錯誤之處還請多多指正。