1. 程式人生 > >java介面學習例子

java介面學習例子

你應該知道介面是一種契約,它與實現方式無關

但是類,即使是抽象類,你都能自定義成員變數,而成員變數往往就與實現方式有關。

這一點的實際意義不大。

但是有一點,類會暴露太多不必要,甚至不能暴露的東西,你看一下java.util中,大部分的資料結構,都被設計成了介面
-抽象類-最後實際類

例如Collection
-List
     L
-AbstractCollection
       L
-AbstractList
         L
-ArrayList

但是有一個,由於歷史原因,被設計成了類,比如Stack 
extends Vector,
你應該知道Stack的資料訪問模式,就只能是LIFO,但是Vector是一個List,可以隨機訪問,可以任意增減。

結果Stack s 
=new Stack();

不光能夠pop
/push/peer還能add,get,set,remove

如果你用一個介面IStack 裡面只有pop
/push/peer方法,然後仍然通過繼承Vector的方式實現,

IStack s 
=new MyStack();

此時,就無法add,get,set,remove

舉個生動一點的例子

publicinterface BritishSpy {
  
public String speak(); //英國間諜講英語
}


publicinterface GermanSpy {
  
public String sprechen(); //德國間諜講德語
}


public
class DoubleAgent implements BritishSpy, GermanSpy {
  
public String speak() return"Hello"; }
  
public String sprechen() return"Gutentag"; }
}


publicclass Agency {
  
publicstaticvoid toMI5(BritishSpy spy) {
    
//軍情5處當然只能說英語,做英國間諜
    spy.speak();
    
//spy.sprechen();不可見
  }


  
publicstaticvoid
 inGermany(GermanSpy spy) {
    
//spy.speak();不可見
    spy.sprechen();
  }


  
publicstaticvoid main(String[] args) {
    DoubleAgent da 
=new DoubleAgent();
    BritishSpy es 
= (BritishSpy) da;
    GermanSpy gs 
= (GermanSpy) da;
    toMI5(da); 
//MI5也不知道他是一個雙重間諜,只知道他是BritishSpy
    toMI5(es); //更安全
    
//toMI5(gs); 不可能
    inGermany(da); //在德國還是安全的,德國人不知道他的雙重間諜身份,只知道他是GermanSpy
    inGermany(gs); 
    
//inGermany(es); 不可能
  }

}


假設你只用class,因為不能多重繼承,所以,speak()
/sprechen()比然宣告在同一個class裡面


publicabstractclass DoubleAgent extends Spy/**(略...)*/{
  
publicabstract String speak();
  
publicabstract String sprechen();
}


publicclass PoorDoubleAgent {
  
public String speak() return"Hello"; }
  
public String sprechen() return"Gutentag"; }
}


晚了,不管你PoorDoubleAgent a 
=new PoorDoubleAgent();還是DoubleAgent a =new PoorDoubleAgent();,全世界都知道他是一個雙重間諜,他到哪裡都必死無疑
前面舉了一個關於“安全性”方面的例子

介面只暴露給對方(比如Agent的軍情5處方法)它所需要的足夠資訊,其他無關的,甚至有害的資訊不會暴露給對方。因為,我傳給你的是介面型別,我除了是這個介面(和這個介面的父介面,inteface A 
extends B, C)的例項外,你頂多知道我是一個Object(不是int:P),其他的姓甚名誰,哪裡住址,父母安好,兄妹幾何都與你無關,我們只需要關心我們簽訂的合同(介面)

再舉一個有關靈活性方面的例子

假設某公司已經有一個更新過N代的,邏輯複雜無比
publicclass A extends B /** where b extends c, c extends d and so on... */{
  
publicvoid init() {...}
  
publicvoid release() {...}
  
public String doXXX() {...}
  
public String doYYY() {...}
}


而這個A又被很多類繼承或使用,doXXX
/doYYY 方法已經無法更改

假設現在這個公司要參加某個標準化組織,而這個組織要求所有提供這樣的方法

String getXXX(); String getYYY();

加入用介面標準化組織只要規定成員們都實現
publicinterface IBusiness {
  String getXXX(); 
  String getYYY();
}

而這個公司只需要稍微改寫一點點即可
publicclass A extends B /** where b extends c, c extends d and so on... */ 
  
implements IBusiness {
  
public String getXXX() return doXXX(); }
  
public String getYYY() return doYYY(); }

//保留
publicvoid init() {...}
  
publicvoid release() {...}
  
public String doXXX() {...}
  
public String doYYY() {...}
}


這樣既滿足了標準化的要求,又滿足了無需修改原來繼承A或者使用A的無數個class(有些可能在使用者那裡,不可能更改)

假如不用介面,你有兩個選擇:數典忘祖或者自絕於人

數典忘祖:
你的新A必須繼承標準化組織的Business,原來a,b, c d...裡面的程式碼全部得重新組織到這個新的A裡面,與此同時,那些呼叫或者繼承A的class難保不需要重寫

自絕於人
原來的就讓它去,誰也別再提它了,我們以後就用新的NewA,結果,你的新客戶倒是滿足了標準化,你的老客戶可就 :
< :@ :$,而且以後需要維護A和NewA
定義介面:
interface Fight{
    
void fight();
}


肥肥和瘦瘦去實現這個介面:
class FatFat implements Fight{
    
publicvoid fight(){
        System.out.println(
"FatFat 打人很痛!");
    }

}


class ThinThin implements Fight{
    
publicvoid fight(){
        System.out.println(
"ThinThin 打人一點都不痛!!哈哈。");
    }

}


然後你可能會這另一個類中使用到FatFat和ThinThin的物件,然後都去執行fight,但是你可能不到執行時就不會知道是具體的那個類的物件,這是你就感謝他們都實現了Fight介面,你可以向上轉型,然後通過執行時的多型產生你想要的行為。

Fight a
=new FatFat();
Fight b
=new ThinThin();
a.fight();
b.fight();
這樣就會執行不同的動作。

或者如果你有一個方法
f(Fight i)
{
   i.fight();
}


如果c是實現了Fight介面的其中一個類,那麼你就可以這樣使用這個方法:
f(c);
你不需要知道c究竟是什麼物件(不管是FatFat還是ThinThin),你都可以得到你想要的fight動作。