簡(jiǎn)單地說(shuō):接口的作用就是把使用接口的人和實(shí)現(xiàn)接口的人分開(kāi),實(shí)現(xiàn)接口的人不必要關(guān)心誰(shuí)去使用幔欧,而使用接口的人也不用關(guān)心實(shí)現(xiàn)的細(xì)節(jié)罪治。
4點(diǎn)關(guān)于JAVA中接口存在的意義:
1、重要性:在Java語(yǔ)言中礁蔗, abstract class 和interface 是支持抽象類定義的兩種機(jī)制觉义。正是由于這兩種機(jī)制的存在,才賦予了Java強(qiáng)大的 面向?qū)ο竽芰Α?/p>
2瘦麸、簡(jiǎn)單谁撼、規(guī)范性:如果一個(gè)項(xiàng)目比較龐大歧胁,那么就需要一個(gè)能理清所有業(yè)務(wù)的架構(gòu)師來(lái)定義一些主要的接口滋饲,這些接口不僅告訴開(kāi)發(fā)人員你需要實(shí)現(xiàn)那些業(yè)務(wù),而且也將命名規(guī)范限制住了(防止一些開(kāi)發(fā)人員隨便命名導(dǎo)致別的程序員無(wú)法看明白)喊巍。
3呵曹、維護(hù)奄喂、拓展性:比如有一個(gè)類跨新,實(shí)現(xiàn)了某個(gè)功能域帐,突然有一天,發(fā)現(xiàn)這個(gè)類滿足不了需求了民假,然后又要重新設(shè)計(jì)這個(gè)類羊异,更糟糕是你可能要放棄這個(gè)類球化,那么其他地方可能有引用他筒愚,這樣修改起來(lái)很麻煩巢掺。
? ? ?如果一開(kāi)始定義一個(gè)接口陆淀,把功能放在接口里轧苫,然后定義類時(shí)實(shí)現(xiàn)這個(gè)接口含懊,然后只要用這個(gè)接口去引用實(shí)現(xiàn)它的類就行了岔乔,以后要換的話只不過(guò)是引用另一個(gè)類而已雏门,這樣就達(dá)到維護(hù)茁影、拓展的方便性募闲。比如有個(gè)method1的方法蝇更,如果用接口年扩,【接口名】 【對(duì)象名】=new 【實(shí)現(xiàn)接口的類】相嵌,這樣想用哪個(gè)類的對(duì)象就可以new哪個(gè)對(duì)象了饭宾,new a()看铆;就是用a的方法弹惦,new b()就是用b的方法棠隐,就和USB接口一樣助泽,插什么讀什么嗡贺,就是這個(gè)原理暑刃。
你要做一個(gè)畫(huà)板程序,其中里面有一個(gè)面板類宵膨,主要負(fù)責(zé)繪畫(huà)功能辟躏,然后你就這樣定義了這個(gè)類捎琐。
4末秃、安全练慕、嚴(yán)密性:接口是實(shí)現(xiàn)軟件松耦合的重要手段铃将,它描敘了系統(tǒng)對(duì)外的所有服務(wù)劲阎,而不涉及任何具體的實(shí)現(xiàn)細(xì)節(jié)。這樣就比較安全雁比、嚴(yán)密一些(一般軟件服務(wù)商考慮的比較多偎捎,jdk中很多方法就是實(shí)現(xiàn)了某個(gè)接口)茴她。
一. 對(duì)接口的三個(gè)疑問(wèn)
很多初學(xué)者都大概清楚interface是1個(gè)什么, 我們可以定義1個(gè)接口, 然后在里面定義一兩個(gè)常量(static final) 或抽象方法.
然后以后寫(xiě)的類就可以實(shí)現(xiàn)這個(gè)接口, 重寫(xiě)里面的抽象方法.?
很多人說(shuō)接口通常跟多態(tài)性一起存在.
接口的用法跟抽象類有點(diǎn)類似.
但是為何要這么做呢.
1.為什么不直接在類里面寫(xiě)對(duì)應(yīng)的方法,? 而要多寫(xiě)1個(gè)接口(或抽象類)?
2.既然接口跟抽象類差不多, 什么情況下要用接口而不是抽象類.
3. 為什么interface叫做接口呢? 跟一般范疇的接口例如usb接口, 顯卡接口有什么聯(lián)系呢?
二. 接口引用可以指向?qū)崿F(xiàn)該接口的對(duì)象
我們清楚接口是不可以被實(shí)例化, 但是接口引用可以指向1個(gè)實(shí)現(xiàn)該接口的對(duì)象.
也就是說(shuō).
假如類A impletments 了接口B
那么下面是合法的:
B b = new A();
也可以把A的對(duì)象強(qiáng)制轉(zhuǎn)換為 接口B的對(duì)象
A a = new A90;
B b = (B)a;
這個(gè)特性是下面內(nèi)容的前提.
三. 抽象類為了多態(tài)的實(shí)現(xiàn).
第1個(gè)答案十分簡(jiǎn)單, 就是為了實(shí)現(xiàn)多態(tài).
下面用詳細(xì)代碼舉1個(gè)例子.
先定義幾個(gè)類,
動(dòng)物(Animal) 抽象類
爬行動(dòng)物(Reptile) 抽象類? 繼承動(dòng)物類
哺乳動(dòng)物(Mammal) 抽象類 繼承動(dòng)物類
山羊(Goat) 繼承哺乳動(dòng)物類
老虎(Tiger)? 繼承哺乳動(dòng)物類
兔子(Rabbit) 繼承哺乳動(dòng)物類
蛇(Snake)?? 繼承爬行動(dòng)物類
農(nóng)夫(Farmer)?? 沒(méi)有繼承任何類 但是農(nóng)夫可以給Animal喂水(依賴關(guān)系)
它們的關(guān)系如下圖:
3.1 Animal類
這個(gè)是抽象類, 顯示也沒(méi)有"動(dòng)物" 這種實(shí)體
類里面包含3個(gè)抽象方法.
1. 靜態(tài)方法getName()
2. 移動(dòng)方法move(), 因?yàn)閯?dòng)物都能移動(dòng).? 但是各種動(dòng)物有不同的移動(dòng)方法, 例如老虎和山羊會(huì)跑著移動(dòng), 兔子跳著移動(dòng), 蛇會(huì)爬著移動(dòng).
作為抽象基類, 我們不關(guān)心繼承的實(shí)體類是如何移動(dòng)的, 所以移動(dòng)方法move()是1個(gè)抽象方法.? 這個(gè)就是多態(tài)的思想.
3. 喝水方法drink(), 同樣, 各種動(dòng)物有各種飲水方法. 這個(gè)也是抽象方法.
代碼:
abstract class Animal{
? ? public abstract String getName();
? ? public abstract void move(String destination);
? ? public abstract void drink();
}
3.2 Mammal類
這個(gè)是繼承動(dòng)物類的哺乳動(dòng)物類, 后面的老虎山羊等都繼承自這個(gè)類.
Mammal類自然繼承了Animal類的3個(gè)抽象方法, 實(shí)體類不再用寫(xiě)其他代碼.
abstract class Mammal extends Animal{
}
3.3 Reptile類
這個(gè)是代表爬行動(dòng)物的抽象類, 同上, 都是繼承自Animal類.
abstract class Reptile extends Animal{
}
3.4 Tiger類
老虎類就是1個(gè)實(shí)體類, 所以它必須重寫(xiě)所有繼承自超類的抽象方法, 至于那些方法如何重寫(xiě), 則取決于老虎類本身.
class Tiger extends Mammal{
? ? private static String name = "Tiger";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Goat moved to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Goat lower it's head and drink.");
? ? }
}
如上, 老虎的移動(dòng)方法很普通, 低頭喝水.
3.5 Goat類 和 Rabbit類
這個(gè)兩個(gè)類與Tiger類似, 它們都繼承自Mammal這個(gè)類.
class Goat extends Mammal{
? ? private static String name = "Goat";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Goat moved to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Goat lower it's head and drink.");
? ? }
}
兔子: 喝水方法有點(diǎn)區(qū)別
class Rabbit extends Mammal{
? ? private static String name = "Rabbit";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Rabbit moved to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Rabbit put out it's tongue and drink.");
? ? }
}
3.6 Snake類
蛇類繼承自Reptile(爬行動(dòng)物)
移動(dòng)方法和喝水方法都跟其他3動(dòng)物有點(diǎn)區(qū)別.
class Snake extends Reptile{
? ? private static String name = "Snake";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Snake crawled to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Snake dived into water and drink.");
? ? }
}
3.7 Farmer 類
Farmer類不屬于 Animal類族, 但是Farmer農(nóng)夫可以給各種動(dòng)物, 喂水.
Farmer類有2個(gè)關(guān)鍵方法, 分別是
bringWater(String destination)??? -> 把水帶到某個(gè)地點(diǎn)
另1個(gè)就是feedWater了,?
feedWater這個(gè)方法分為三步:
首先是農(nóng)夫帶水到飼養(yǎng)室,(bringWater())
接著被喂水動(dòng)物走到飼養(yǎng)室,(move())
接著動(dòng)物喝水(drink())
Farmer可以給老虎喂水, 可以給山羊喂水, 還可以給蛇喂水, 那么feedWater()里的參數(shù)類型到底是老虎,山羊還是蛇呢.
實(shí)際上因?yàn)槔匣?山羊, 蛇都繼承自Animal這個(gè)類, 所以feedWater里的參數(shù)類型設(shè)為Animal就可以了.
Farmer類首先叼用bringWater("飼養(yǎng)室"),
至于這個(gè)動(dòng)物是如何走到飼養(yǎng)室和如何喝水的, Farmer類則不用關(guān)心.
因?yàn)閳?zhí)行時(shí), Animal超類會(huì)根據(jù)引用指向的對(duì)象類型不同 而 指向不同的被重寫(xiě)的方法.? 這個(gè)就是多態(tài)的意義.
代碼如下:
class Farmer{
? ? public void bringWater(String destination){
? ? ? ? System.out.println("Farmer bring water to " + destination + ".");
? ? }
? ? public void feedWater(Animal a){ // polymorphism
? ? ? ? this.bringWater("Feeding Room");
? ? ? ? a.move("Feeding Room");
? ? ? ? a.drink();
? ? }
}
3.7 執(zhí)行農(nóng)夫喂水的代碼.
下面的代碼是1個(gè)農(nóng)夫依次喂水給一只老虎, 一只羊, 以及一條蛇
public static void f(){
? ? ? ? Farmer fm = new Farmer();
? ? ? ? Snake sn = new Snake();
? ? ? ? Goat gt = new Goat();
? ? ? ? Tiger tg = new Tiger();
? ? ? ? fm.feedWater(sn);
? ? ? ? fm.feedWater(gt);
? ? ? ? fm.feedWater(tg);
? ? }
農(nóng)夫只負(fù)責(zé)帶水過(guò)去制定地點(diǎn), 而不必關(guān)心老虎, 蛇, 山羊它們是如何過(guò)來(lái)的. 它們?nèi)绾魏人? 這些農(nóng)夫都不必關(guān)心.
只需要調(diào)用同1個(gè)方法feedWater.??
執(zhí)行結(jié)果:
? [java] Farmer bring water to Feeding Room.
? ? [java] Snake crawled to Feeding Room.
? ? [java] Snake dived into water and drink.
? ? [java] Farmer bring water to Feeding Room.
? ? [java] Goat moved to Feeding Room.
? ? [java] Goat lower it's head and drink.
? ? [java] Farmer bring water to Feeding Room.
? ? [java] Goat moved to Feeding Room.
? ? [java] Goat lower it's head and drink.
不使用多態(tài)的后果?:
而如果老虎, 蛇, 山羊的drink() 方法不是重寫(xiě)自同1個(gè)抽象方法的話, 多態(tài)就不能實(shí)現(xiàn).
農(nóng)夫類就可能要根據(jù)參數(shù)類型的不同而重載很多個(gè)? feedWater()方法了.
而且每增加1個(gè)類(例如 獅子Lion)
就需要在農(nóng)夫類里增加1個(gè)feedWater的重載方法 feedWater(Lion l)...
而接口跟抽象類類似,
這個(gè)就回答了不本文第一個(gè)問(wèn)題.
1.為什么不直接在類里面寫(xiě)對(duì)應(yīng)的方法,? 而要多寫(xiě)1個(gè)接口(或抽象類)?
四. 抽象類解決不了的問(wèn)題.
既然抽象類很好地實(shí)現(xiàn)了多態(tài)性, 那么什么情況下用接口會(huì)更加好呢?
對(duì)于上面的例子, 我們加一點(diǎn)需求.
Farmer 農(nóng)夫多了1個(gè)技能, 就是給另1個(gè)動(dòng)物喂兔子(囧).
BringAnimal(Animal a, String destination)???? 把兔子帶到某個(gè)地點(diǎn)...
feedAnimal(Animal ht, Animal a)??????????? 把動(dòng)物a丟給動(dòng)物ht
注意農(nóng)夫并沒(méi)有把兔子宰了, 而是把小動(dòng)物(a)丟給另1個(gè)被喂食的動(dòng)物(ht).
那么問(wèn)題來(lái)了, 那個(gè)動(dòng)物必須有捕獵這個(gè)技能.? 也就是我們要給被喂食的動(dòng)物加上1個(gè)方法(捕獵) hunt(Animal a).
但是現(xiàn)實(shí)上不是所有動(dòng)物都有捕獵這個(gè)技能的, 所以我們不應(yīng)該把hunt(Animal a)方法加在Goat類和Rabbit類里,? 只加在Tiger類和Snake類里.
而且老虎跟蛇的捕獵方法也不一樣, 則表明hunt()的方法體在Tiger類里和Snake類里是不一樣的.
下面有3個(gè)方案.
1. 分別在Tiger類里和Snake類里加上Hunt() 方法.? 其它類(例如Goat) 不加.
2. 在基類Animal里加上Hunt()抽象方法. 在Tiger里和Snake里重寫(xiě)這個(gè)Hunt() 方法.
3. 添加肉食性動(dòng)物這個(gè)抽象類.???
先來(lái)說(shuō)第1種方案.
這種情況下, Tiger里的Hunt(Animal a)方法與 Snake里的Hunt(Animal a)方法毫無(wú)關(guān)聯(lián). 也就是說(shuō)不能利用多態(tài)性.
導(dǎo)致Farm類里的feedAnimal()方法需要分別為Tiger 與 Snake類重載. 否決.
第2種方案:
如果在抽象類Animal里加上Hunt()方法, 則所有它的非抽象派生類都要重寫(xiě)實(shí)現(xiàn)這個(gè)方法, 包括 Goat類和 Rabbit類.
這是不合理的, 因?yàn)镚oat類根本沒(méi)必要用到Hunt()方法, 造成了資源(內(nèi)存)浪費(fèi).
第3種方案:
加入我們?cè)诓溉轭悇?dòng)物下做個(gè)分叉, 加上肉食性哺乳類動(dòng)物, 非肉食性哺乳動(dòng)物這兩個(gè)抽象類?
首先,
肉食性這種分叉并不準(zhǔn)確, 例如很多腐蝕性動(dòng)物不會(huì)捕獵, 但是它們是肉食性.
其次
這種方案會(huì)另類族圖越來(lái)越復(fù)雜, 假如以后再需要辨別能否飛的動(dòng)物呢, 增加飛翔 fly()這個(gè)方法呢? 是不是還要分叉?
再次,
很現(xiàn)實(shí)的問(wèn)題, 在項(xiàng)目中, 你很可能沒(méi)機(jī)會(huì)修改上層的類代碼, 因?yàn)樗鼈兪怯肑ar包發(fā)布的, 或者你沒(méi)有修改權(quán)限.
這種情況下就需要用到接口了.
五.接口與多態(tài) 以及 多繼承性.
上面的問(wèn)題, 抽象類解決不了, 根本問(wèn)題是Java的類不能多繼承.
因?yàn)門iger類繼承了動(dòng)物Animal類的特性(例如 move() 和 drink()) , 但是嚴(yán)格上來(lái)將 捕獵(hunt())并不算是動(dòng)物的特性之一. 有些植物, 單細(xì)胞生物也會(huì)捕獵的.
所以Tiger要從別的地方來(lái)繼承Hunt()這個(gè)方法.? 接口就發(fā)揮作用了.
修改后的UML圖如下:
5.1 Huntable接口
由UML圖可知,
我們?cè)黾恿?個(gè)Huntable接口.
接口里有1個(gè)方法hunt(Animal a), 就是捕捉動(dòng)物, 至于怎樣捕捉則由實(shí)現(xiàn)接口的類自己決定.
代碼:
interface Huntable{
? ? public void hunt(Animal a);
}
5.2 Tiger 類
既然定義了1個(gè)Huntable(可以捕獵的)接口.
Tiger類就要實(shí)現(xiàn)這個(gè)接口并重寫(xiě)接口里hunt()方法.
class Tiger extends Mammal implements Huntable{
? ? private static String name = "Tiger";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Goat moved to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Goat lower it's head and drink.");
? ? }
? ? public void hunt(Animal a){
? ? ? ? System.out.println("Tiger catched " + a.getName() + " and eated it");
? ? }
}
5.3 Snake類
同樣:
class Snake extends Reptile implements Huntable{
? ? private static String name = "Snake";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Snake crawled to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Snake dived into water and drink.");
? ? }
? ? public void hunt(Animal a){
? ? ? ? System.out.println("Snake coiled " + a.getName() + " and eated it");
? ? }
}
可見(jiàn)同樣實(shí)現(xiàn)接口的hunt()方法, 但是蛇與老虎的捕獵方法是有區(qū)別的.
5.4 Farmer類
這樣的話. Farmer類里的feedAnimal(Animal ht, Animal a)就可以實(shí)現(xiàn)多態(tài)了.
class Farmer{
? ? public void bringWater(String destination){
? ? ? ? System.out.println("Farmer bring water to " + destination + ".");
? ? }
? ? public void bringAnimal(Animal a,String destination){
? ? ? ? System.out.println("Farmer bring " + a.getName() + " to " + destination + ".");
? ? }
? ? public void feedWater(Animal a){
? ? ? ? this.bringWater("Feeding Room");
? ? ? ? a.move("Feeding Room");
? ? ? ? a.drink();
? ? }
? ? public void feedAnimal(Animal ht , Animal a){
? ? ? ? this.bringAnimal(a,"Feeding Room");
? ? ? ? ht.move("Feeding Room");
? ? ? ? Huntable hab = (Huntable)ht;
? ? ? ? hab.hunt(a);
? ? }
}
關(guān)鍵是這一句
Huntable hab = (Huntable)ht;
本文一開(kāi)始講過(guò)了, 接口的引用可以指向?qū)崿F(xiàn)該接口的對(duì)象.
當(dāng)然, 如果把Goat對(duì)象傳入Farmer的feedAnimal()里就會(huì)有異常, 因?yàn)镚oat類沒(méi)有實(shí)現(xiàn)該接口. 上面那個(gè)代碼執(zhí)行失敗.
如果要避免上面的問(wèn)題.
可以修改feedAnimal方法:
? ? public void feedAnimal(Huntable hab, Animal a){
? ? ? ? this.bringAnimal(a,"Feeding Room");
? ? ? ? Animal ht = (Animal)hab;
? ? ? ? ht.move("Feeding Room");
? ? ? ? hab.hunt(a);
? ? }
這樣的話, 傳入的對(duì)象就必須是實(shí)現(xiàn)了Huntable的對(duì)象, 如果把Goat放入就回編譯報(bào)錯(cuò).
但是里面一樣有一句強(qiáng)制轉(zhuǎn)換
Animal ht = (Animal)hab
反而更加不安全, 因?yàn)閷?shí)現(xiàn)的Huntable的接口的類不一定都是Animal的派生類.
相反, 接口的出現(xiàn)就是鼓勵(lì)多種不同的類實(shí)現(xiàn)同樣的功能(方法)
例如,假如一個(gè)機(jī)械類也可以實(shí)現(xiàn)這個(gè)接口, 那么那個(gè)機(jī)械就可以幫忙打獵了(囧)
1個(gè)植物類(例如捕蠅草),實(shí)現(xiàn)這個(gè)接口, 也可以捕獵蒼蠅了.
也就是說(shuō), 接口不會(huì)限制實(shí)現(xiàn)接口的類的類型.
執(zhí)行輸出:
? ? [java] Farmer bring Rabbit to Feeding Room.
? ? [java] Snake crawled to Feeding Room.
? ? [java] Snake coiled Rabbit and eated it
? ? [java] Farmer bring Rabbit to Feeding Room.
? ? [java] Goat moved to Feeding Room.
? ? [java] Tiger catched Rabbit and eated it
這樣, Tiger類與Snake類不但繼承了Animal的方法, 還繼承(實(shí)現(xiàn))了接口Huntable的方法, 一定程度上彌補(bǔ)java的class不支持多繼承的特點(diǎn).
六.接口上應(yīng)用泛型.
上面的Huntable里還是有點(diǎn)限制的,
就是它里面的hunt()方法的參數(shù)是 Animal a, 也就是說(shuō)這個(gè)這個(gè)接口只能用于捕獵動(dòng)物.
但是在java的世界里, 接口里的方法(行為)大多數(shù)是與類的類型無(wú)關(guān)的.
也就是說(shuō), Huntable接口里的hunt()方法里不單只可以捕獵動(dòng)物, 還可以捕獵其他東西(例如 捕獵植物... 敵方機(jī)械等)
6.1 Huntable接口
首先要在Huntable接口上添加泛型標(biāo)志:<T>
interface Huntable<T>{
? ? public void hunt(T o);
}
然后里面的hunt()的參數(shù)的類型就寫(xiě)成T, 表示hunt()方法可以接受多種參數(shù), 取決于實(shí)現(xiàn)接口的類.
6.2 Tiger類(和Snake類)
同樣, 定義tiger類時(shí)必須加上接口的泛型標(biāo)志<Animal>, 表示要把接口應(yīng)用在Animal這種類型.
class Tiger extends Mammal implements Huntable<Animal>{
? ? private static String name = "Tiger";
? ? public String getName(){
? ? ? ? return this.name;
? ? }
? ? public void move(String destination){
? ? ? ? System.out.println("Goat moved to " + destination + ".");
? ? }
? ? public void drink(){
? ? ? ? System.out.println("Goat lower it's head and drink.");
? ? }
? ? public void hunt(Animal a){
? ? ? ? System.out.println("Tiger catched " + a.getName() + " and eated it");
? ? }
}
這樣, 在里面hunt()參數(shù)就可以指明類型Animal了,? 表示老虎雖然有捕獵這個(gè)行為, 但是只能捕獵動(dòng)物.
七.什么情況下應(yīng)該使用接口而不用抽象類.
好了, 回到本文最重要的一個(gè)問(wèn)題.
做個(gè)總結(jié)
1. 需要實(shí)現(xiàn)多態(tài)
2. 要實(shí)現(xiàn)的方法(功能)不是當(dāng)前類族的必要(屬性).
3. 要為不同類族的多個(gè)類實(shí)現(xiàn)同樣的方法(功能).
下面是分析:
7.1 需要實(shí)現(xiàn)多態(tài)
很明顯, 接口其中一個(gè)存在意義就是為了實(shí)現(xiàn)多態(tài). 這里不多說(shuō)了.
而抽象類(繼承) 也可以實(shí)現(xiàn)多態(tài)
7.2. 要實(shí)現(xiàn)的方法(功能)不是當(dāng)前類族的必要(屬性).
上面的例子就表明, 捕獵這個(gè)方法不是動(dòng)物這個(gè)類必須的,
在動(dòng)物的派生類中, 有些類需要, 有些不需要.??
如果把捕獵方法卸載動(dòng)物超類里面是不合理的浪費(fèi)資源.
所以把捕獵這個(gè)方法封裝成1個(gè)接口, 讓派生類自己去選擇實(shí)現(xiàn)!
7.3. 要為不同類族的多個(gè)類實(shí)現(xiàn)同樣的方法(功能).
上面說(shuō)過(guò)了, 其實(shí)不是只有Animal類的派生類才可以實(shí)現(xiàn)Huntable接口.
如果Farmer實(shí)現(xiàn)了這個(gè)接口, 那么農(nóng)夫自己就可以去捕獵動(dòng)物了...
我們拿另個(gè)常用的接口Comparable來(lái)做例子.
這個(gè)接口是應(yīng)用了泛型,
首先, 比較(CompareTo) 這種行為很難界定適用的類族, 實(shí)際上, 幾乎所有的類都可以比較.
比如 數(shù)字類可以比較大小,?? 人類可以比較財(cái)富,? 動(dòng)物可以比較體重等.
所以各種類都可以實(shí)現(xiàn)這個(gè)比較接口.
一旦實(shí)現(xiàn)了這個(gè)比較接口. 就可以開(kāi)啟另1個(gè)隱藏技能:
就是可以利用Arrays.sort()來(lái)進(jìn)行排序了.
就如實(shí)現(xiàn)了捕獵的動(dòng)物,
可以被農(nóng)夫Farmer喂兔子一樣...
八.接口為什么會(huì)被叫做接口, 跟真正的接口例如usb接口有聯(lián)系嗎?
對(duì)啊, 為什么叫接口, 而不叫插件(plugin)呢,? 貌似java接口的功能更類似1個(gè)插件啊.
插上某個(gè)插件, 就有某個(gè)功能啊.
實(shí)際上, 插件與接口是相輔相成的.
例如有1個(gè)外部存儲(chǔ)插件(U盤), 也需要使用設(shè)備具有usb接口才能使用啊.
再舉個(gè)具體的例子.
個(gè)人電腦是由大型機(jī)發(fā)展而來(lái)的
大型機(jī)->小型機(jī)->微機(jī)(PC)
而筆記本是繼承自微機(jī)的.
那么問(wèn)題來(lái)了.
對(duì)于, 計(jì)算機(jī)的CPU/內(nèi)存/主板/獨(dú)顯/光驅(qū)/打印機(jī) 有很多功能(方法/行為), 那么到底哪些東西是繼承, 哪些東西是接口呢.
首先,? cpu/內(nèi)存/主板 是從大型機(jī)開(kāi)始都必備的, 任何計(jì)算機(jī)都不能把它們?nèi)サ?
所以, 這三樣?xùn)|西是繼承的, 也就說(shuō)筆記本的cpu/內(nèi)存/主板是繼承自微機(jī)(PC)的
但是/光驅(qū)/呢,??? 現(xiàn)實(shí)上很多超薄筆記本不需要光驅(qū)的功能.
如果光驅(qū)做成繼承, 那么筆記本就必須具有光驅(qū), 然后屏蔽光驅(qū)功能, 那么這臺(tái)筆記本還能做到超薄嗎? 浪費(fèi)了資源.
所以光驅(qū),打印機(jī)這些東西就應(yīng)該做成插件.
然后, 在筆記本上做1個(gè)可以插光驅(qū)和打印機(jī)的接口(usb接口).
也就是說(shuō), PC的派生類, 有些(筆記本)可以不實(shí)現(xiàn)這個(gè)接口, 有些(臺(tái)式機(jī))可以實(shí)現(xiàn)這個(gè)接口,只需要把光驅(qū)插到這個(gè)接口上.
至于光驅(qū)是如何實(shí)現(xiàn)的,
例如一些pc派生類選擇實(shí)現(xiàn)藍(lán)光光驅(qū), 有些選擇刻錄機(jī).? 但是usb接口本身并不關(guān)心. 取決與實(shí)現(xiàn)接口的類.
這個(gè)就是現(xiàn)實(shí)意義上的多態(tài)性啊.