Java8新特性之四:介面預設方法和靜態方法
在JDK1.8以前,介面(interface)沒有提供任何具體的實現,在《JAVA程式設計思想》中是這樣描述的:“interface這個關鍵字產生了一個完全抽象的類,它根本就沒有提供任何具體的實現。它允許建立者確定方法名、引數列表和返回型別,但是沒有任何方法體。介面只提供了形式,而未提供任何具體實現”。
但是這一限制在JDK1.8中被打破了,JDK1.8開始,介面允許定義預設方法和靜態方法。
介面預設方法的語法很簡單,即:
default關鍵字 methodName(引數列表) { // 實現體 }
介面靜態方法語法與類的靜態方法類似,不同的是介面靜態方法的修飾符只能是public。
1、預設方法
為了提高程式碼的可重用性。介面的預設方法有助於在擴充套件系統功能的同時,不對現有的繼承關係及類庫產生很大的影響。例如在JDK1.8中,Java集合框架的Collection介面增加了stream()等預設方法,這些預設方法即增強了集合的功能,又能保證對低版本的JDK的相容。
舉個簡單的例子,假如有一個Animal介面其中有fly()和swim()方法,有一個鳥類Bird和一個魚類Fish同時實現這個介面,程式碼如下:
Animal介面:
1 public interface Animal {
2 void run();
3 void swim();
4 }
Bird.java
public class Bird implements Animal {
@Override
public void swim() {
// do nothing
}
@Override
public void fly() {
System.out.println("birds can fly...");
}
}
Fish.java
public class Fish implements Animal {
@Override
public void swim() {
System.out.println("fish can swim......");
}
@Override
public void fly() {
// donothing
}
}
從上程式碼可以看到,因為Animal中定義了fly()和swim()方法,所以所有實現它的類都要覆寫這兩個方法,在Bird類中,鳥會飛,不會游泳,但是又必須要實現swim()方法,Fish類不會飛,但是又必須要實現fly()方法。程式碼出現冗餘。
假如現在又有了新的需求,需要在Animal介面中再增加一個cry()方法,那麼之前所有實現了Animal介面的方法勢必都在再覆寫cry()方法,整個系統中可能會有很多地方需要同步修改,而此時,default方法和靜態方法就顯得尤為必要了。
改寫上面的例子:
Animal.java
public interface Animal {
default void fly() {
System.out.println("birds can fly...");
}
default void swim() {
System.out.println("fishes can swim......");
}
}
Bird.java
1 public class Bird implements Animal {
2 }
Fish.java
1 public class Fish implements Animal {
2 }
測試類:
public class TestMain {
public static void main(String[] args) {
Bird bird=new Bird();
bird.fly();
Fish fish=new Fishe();
fish.swim();
}
}
執行結果:
birds can fly...
fishes can swim......
從修改後程式碼可以看出,程式碼得到了複用,Animal實現類中也沒有了冗餘。
2、靜態方法
假如有一個Animal工廠介面,該介面中有一個靜態方法create()專門生產不同的Animal,在JDK1.8後由於引入了Lambda表示式,使子類不用覆寫該介面的create()方法也可以生產任意的Animal,程式碼如下:
1 public interface AnimalFactory {
2
3 static Animal create(Supplier supplier) {
4 return supplier.get();
5 }
6 }
測試類:
public class TestAnimalFactory {
public static void main(String[] args) {
// 生產一隻鳥
Animal bird=AnimalFactory.create(Bird::new);
bird.fly();
// 生產一條魚
Animal fish=AnimalFactory.create(Fishe::new);
fish.swim();
}
}
執行結果:
birds can fly...
fishes can swim......
3、介面靜態方法的“類優先”原則
如果一個介面實現類提供了具體的實現,那麼介面中具有相同名稱和引數的預設方法會被忽略,如改寫之前的Bird類:
1 public class Bird implements Animal {
2
3 public void fly() {
4 System.out.println("Bird類中的fly方法:birds can fly...");
5 }
6 }
測試類:
public class TestMain {
public static void main(String[] args) {
Bird bird=new Bird();
bird.fly();
}
}
執行結果:
Bird類中的fly方法:birds can fly...
可見,呼叫的是Bird類中自己的fly()方法而不是Animal介面中的預設方法。
4、介面衝突
假如一個類實現了兩個介面,兩個介面中都有同樣的預設方法,哪個是有效的?
答案是:兩個都無效!
該類必須要覆該方法來解決衝突,否則編譯器將會報錯。