2012-01-10 20 views
1

所以我試圖瞭解接口,但我幾乎只看到文章,解釋「如何」使用的接口,我的問題是理解「爲什麼」:
所以最好是使用接口不是創造和繼承一個類,這可能是無用的,
所以我們實現在類的接口方法,但我不明白爲什麼這是一件好事,的接口和契約:在這個例子中

比方說, :
類似Car.java定義了所有的代碼,使汽車
我們創建幾種方法如啓動界面Working.java(),停止()等
我們實施的方法Diesel_Car.javaElectric_Car.java
那麼什麼改變Car.java?這可能不是最好的例子,因爲看起來Car應該是Diesel_Car.java等的母公司,
但是在這些類中實現這些方法有什麼意義?
有沒有方法Car.java,以某種方式「調用」Diesel_Car.java類和它的接口方法?

我讀過界面就像是一個「契約」,但我只看到了這個契約的第二部分(這個方法實現的地方),我有一些麻煩想象第一部分發生在哪裏?

感謝您的幫助

+0

[Class vs. Interface]的可能重複(http://stackoverflow.com/questions/2271104/class-vs-interface) – 2012-01-10 13:23:56

+0

「第一部分」是合同的*定義*,第二部分是實施。這是一件「好事」,因爲它意味着功能與實現無關(我可以將每個實現視爲Car),並且類可以實現多個接口。 – 2012-01-10 13:24:45

+0

此問題之前已被問過,請參閱http://stackoverflow.com/questions/416331/java-interfaces – Peter 2012-01-10 13:24:49

回答

1

讓我們把你的基類的Car的例子有Electric_CarDiesel_Car子類,並擴展模型位。

車可能具有以下接口

  1. Working:與start() & stop()方法
  2. Moving:與move()turn() & stop()方法

Car可能包含類的一個實例這也應該實現接口Working

Driver對象可以與對象進行交互,而不是實現Working,驅動程序可以是start()stop()。 (司機可以獨立啓動或停止汽車和空調)。

另外,由於Driver可以自己走動(並且並不總是需要一輛車),所以他應該實現接口Moving。 對象Ground現在可以與任何實現Moving的任何事物交互:無論是汽車還是司機。

+0

感謝Nitzan,我想我已經開始更多地理解它了,但我仍然有點失落。你會有另一個例子來描述接口嗎?我真的很感激 – Paul 2012-01-10 14:07:23

+0

我會試着想想另一個例子, 仍然試圖明白你到底有什麼困難。在上面的例子中,你仍然缺少什麼? – 2012-01-10 18:52:39

+0

我想知道在什麼情況下地面需要處理一輛車,然後改變汽車的類型爲司機。這更多的是使用界面的「爲什麼」。 – Paul 2012-01-10 18:59:48

0

合同是一個關於類如何相互工作的概念。這個想法是接口類定義了方法的返回類型和名稱,但沒有提供如何實現它的想法。這是由實施課程完成的。

概念是,當接口A定義方法A和B時,任何實現該接口的類必須實現A和B以及它自己的方法。所以它可能會這樣工作:

interface InterfaceA { 
    void methodA(); 
    void methodB(String s); 
} 

public class ClassA implements InterfaceA { 
    public void methodA() { 
     System.out.println("MethodA"); 
    } 

    public void methodB(String s) { 
     System.out.println(s); 
    } 
} 

合同原則是任何實現接口的實現都必須實現整個接口。任何不這樣做的東西都必須是抽象的。

希望這會有所幫助。

+0

感謝MikeyB,這是我們應該做什麼來使用界面,但不是如何工作,接下來當方法在類中實現時會發生什麼...... – Paul 2012-01-10 13:45:29

+0

@Paul你是什麼意思,「接下來會發生什麼「?你問的是如何Java方法調度工作? – 2012-01-10 18:30:34

+0

@Dave Newton:好吧,看起來主要的一點是能夠改變我們正在處理的對象的類型(通過將接口指定爲對象的類型),但爲什麼要這樣做呢? – Paul 2012-01-10 19:05:02

0

按合同設計(DbC),也稱爲按合同編寫程序和按合同設計編程,是設計計算機軟件的一種方法。它規定軟件設計者應該爲軟件組件定義形式化,精確和可驗證的接口規範,這些規範擴展了具有前置條件,後置條件和不變量的抽象數據類型的普通定義。根據與商業合同條件和義務的概念性比喻,這些規範被稱爲「合同」。 Wikipedia

捷徑。

如果您遵循對接口進行編碼的良好做法,那麼您知道接口定義了所有實現類必須遵守的契約。

我們設計了Contract Java,它是在接口中指定方法合約的Java的擴展。我們確定了三個設計目標。

  • 首先,合同Java程序沒有與 完全滿足合同合同和方案應該表現爲,如果他們不需要在Java 合約運行。其次,使用傳統Java編譯器編譯的程序必須能夠與在Contract Java下編譯的程序 進行互操作。
  • 最後,除非一個班級宣稱它符合特定的合同,否則不應該因爲未能符合該合同而被指責。抽象地說,如果調用類型爲t的對象的方法m,則僅應將責備者指定爲與t關聯的前置條件合同,並且應該僅針對與t關聯的後置條件合同 來指責m。 這些設計目標引發了一些有趣的問題,並要求決策權衡軟件工程問題的語言設計。本節介紹每個主要設計問題,備選方案,我們的決定,我們的理由以及決策的影響。決定不是正交的;一些後來的決定 依賴於以前的決定。

契約中的契約Java是接口中方法契約的裝飾。每個方法聲明可能帶有前置條件表達式和後置條件表達式 ;這兩個表達式都必須評估爲布爾值。前提條件指定調用該方法時必須滿足的條件。如果失敗,該方法調用的上下文將被指定爲 ,因爲沒有在適當的上下文中使用該方法。後置條件表達式指定該方法返回時必須成立的條件。如果它失敗了,那麼這個方法本身就成了沒有建立承諾條件的原因。 合約Java不限制合約表達式。儘管如此,良好的編程規範仍然規定表達式 不應該對程序的結果有所貢獻。特別是,表達式不應該有任何副作用。 前後條件表達式都是通過方法的參數和僞變量 來參數化的。後者與當前對象綁定。此外,合同的後續條件可能涉及方法的名稱,該方法與方法調用的結果綁定。 基於方法調用的類型上下文強制執行合同。如果對象的類型是接口類型,那麼方法調用必須滿足接口中的所有合同。例如,如果一個對象實現接口I,則對我方法之一的呼叫 必須檢查在I中指定的先決條件和後置條件。如果對象的類型是類別 類型,則該對象沒有合同義務。由於程序員總是可以爲任何類創建一個接口,所以我們 爲了效率的原因不選擇類類型的對象。 舉一個例子,考慮接口RootFloat:

interface RootFloat { 
    float getValue(); 
    float sqRoot(); 
    @pre { this.getValue() >= 0f } 
    @post { Math.abs(sqRoot * sqRoot - this.getValue()) < 0.01f } 
} 

它描述了一個浮子包裝類,提供了一種方法sqRoot接口。第一種方法getValue沒有 合同。它不接受任何參數並返回解包的浮點數。 sqRoot方法也不接受參數, 但有合同。前提條件斷言展開的值大於或等於零。 sqRoot的結果類型 是float。後置條件表明結果的平方必須在浮點值的0.01以內。 即使合同語言足夠強大以在某些情況下指定完整的行爲,例如前面的示例,但在設計這些合同時,全部甚至部分正確不是我們的目標。通常,合同 無法表達方法的完整行爲。事實上,界面中顯示的信息量與合同能夠滿足的驗證數量之間存在緊張關係。 舉一個例子,考慮這個堆棧接口:

interface Stack { 
    void push (int i); 
    int pop(); 
} 

在界面中僅push和pop操作,這是不可能指定,一推之後,在堆棧頂部 元素是元素剛被推。但是,如果我們有一個頂級的操作, 顯示堆棧上的最上面的項目(不將其移除)增強的界面,那麼我們可以指定推增加了項目到 堆棧的頂部:

interface Stack { 
    void push (int x); 
    @post { x = this.top() } 
    int pop(); 
    int top(); 
} 

在總結,我們不限制合同的語言。這使合同語言儘可能靈活;合同表達評估甚至可能有助於計算的最終結果。儘管合同語言具有靈活性,但並非所有可取的合同都是可以表達的。一些合同是不可壓縮的,因爲它們可能涉及檢查不可判定的屬性,而另一些合同是不可壓縮的,因爲界面不允許有足夠的觀察結果。

+0

雖然這是Java本身的很遠的地方。 – 2012-01-10 14:25:37

1

(很)人爲的例子(爲了清楚起見,非泛型,刪除了錯誤處理等)。

List theList = new ArrayList(); 

theListList,在這種情況下,由一個ArrayList實現。比方說,我們將其傳遞給第三方API,這個API在某個地方添加了一些內容。

public void frobList(List list) { 
    list.add(new Whatever()); 
} 

現在,讓我們說出於某種原因,我們希望對添加到列表中的項目執行一些不尋常的操作。我們無法更改第三方API。但是,我們可以創建一個新的列表類型。

public FrobbableList extends ArrayList { 
    public boolean add(E e) { 
     super.add(Frobnicator.frob(e)); 
    } 
} 

現在,在我們的代碼,我們改變我們實例列表,並調用API像以前一樣:

List theList = new FrobbableList(); 
frobber.frobList(theList); 

如果第三方API已經採取了ArrayList(實際類型),而不是一個List的(界面),我們無法輕鬆做到這一點。通過不將API鎖定到特定的實現中,它爲我們提供了創建自定義行爲的機會。此外,這是一個可擴展,可調試,可測試代碼的基礎概念。像dependency injection/Inversion of Control這樣的東西依靠編碼來實現接口功能。

+0

感謝戴夫,我想我終於明白你的例子了,謝謝你的回答;-) – Paul 2012-01-11 14:06:38

1

我正在做另一次嘗試來解釋接口作爲契約的概念。

典型使用場景是,當你想使用排序元素java.util.Collections的列表:<T extends java.lang.Comparable<? super T>> void sort(java.util.List<T> ts)

是什麼意思簽名? sort()方法將接受T類型對象的java.util.List<T>,其中T是實現接口Comparable的對象。

所以,如果你想與你的對象列表使用Collections.sort(),你會需要它們來實現Comparable接口:

public interface Comparable<T> { 
    int compareTo(T t); 
} 

所以,如果你實現一個類Car類型並且想要通過使用Collections.sort()來比較汽車的重量,您必須在班級汽車中實施Comparable接口/合同。

public class Car implements Comparable<Car> { 
    private int weight; 

    //..other class implementation stuff 

    @Override 
    public int compareTo(Car otherCar) { 
     if (this.weight == otherCar.weight) return 0; 
     else if (this.weight > otherCar.weight) return 1; 
     else return -1; 
    } 
} 

底下的內容Collections.sort()會在您對sortTo排序列表時調用您的實現。

+0

沒問題,所以這個方法就像一個回調,處理這個回調的代碼已經寫在其他地方了...好吧,我想我多瞭解一點,非常感謝你的回答! – Paul 2012-01-11 14:10:49