java介面學習例子
阿新 • • 發佈:2019-01-08
你應該知道介面是一種契約,它與實現方式無關
但是類,即使是抽象類,你都能自定義成員變數,而成員變數往往就與實現方式有關。
這一點的實際意義不大。
但是有一點,類會暴露太多不必要,甚至不能暴露的東西,你看一下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動作。
但是類,即使是抽象類,你都能自定義成員變數,而成員變數往往就與實現方式有關。
這一點的實際意義不大。
但是有一點,類會暴露太多不必要,甚至不能暴露的東西,你看一下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
public String speak() { return"Hello"; }
public String sprechen() { return"Gutentag"; }
}
publicclass Agency {
publicstaticvoid toMI5(BritishSpy spy) {
//軍情5處當然只能說英語,做英國間諜
spy.speak();
//spy.sprechen();不可見
}
publicstaticvoid
//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動作。