默認方法是啥
默認方法是由default修飾符修飾躬翁,并像類中聲明的其他方法一樣包含方法體枉长。是Java8中新添加的能力。
默認方法能干啥
可選方法
默認方法的主要應用場景在定義API上齐莲,可以避免臨時往接口里面加方法導致的尷尬放祟。
舉一個身邊的例子。比如Android升級的時候往接口里加了個方法串结,但是方法又不常用哑子,升級的時候我們就不得不實現(xiàn)一下這個方法然后加一個空實現(xiàn)或者拋出一個未支持異常。但是現(xiàn)在有了默認方法肌割,這種場景下Android就可以寫一個默認方法卧蜓,不用我們非得實現(xiàn)一個我們不會去使用的接口方法了。
總之把敞,對于那些不是一定要實現(xiàn)的接口方法弥奸,我們可以避免在實現(xiàn)的時候刻意留白的情況出現(xiàn)。
函數(shù)式接口
之前講過奋早,函數(shù)式接口是只包含一個抽象方法的接口盛霎。這句話在以前不需要過多考慮,但是現(xiàn)在有了默認方法就不一樣了耽装。因為默認方法不是抽象方法愤炸,所以我們多些幾個默認方法是不影響的。實際上現(xiàn)在的函數(shù)式接口Predicate掉奄、Function以及Comparator確實也引入了默認方法规个。
行為的多繼承
當年學Java的時候,學到繼承的位置需要特別注意的一點是Java不像C++一樣支持行為多繼承。現(xiàn)在不一樣了诞仓,有了默認方法大家可以在接口里寫實現(xiàn)了缤苫,這時實現(xiàn)多個接口我們自然就擁有它們各個接口的能力了。
利用這一點墅拭,我們可以把能夠解決獨立能力的通用代碼抽成接口榨馁,并把需要的能力用默認方法實現(xiàn)。當我們想要用的這些能力的時候?qū)崿F(xiàn)一下接口就可以了帜矾。這種方式與模版設計模式類似。
默認方法存在啥問題
方法名沖突
在默認方法之前如果實現(xiàn)兩個有重名的方法的接口屑柔,對于重名的接口方法實現(xiàn)成一個就好了屡萤。但是現(xiàn)在有了默認方法,如果兩個接口中有同名的默認方法掸宛,那么在同時實現(xiàn)這兩個接口的時候會選擇使用哪一個方法呢死陆?這種情況有極低可能會出現(xiàn),但還是要有規(guī)則來處理這種問題唧瘾。
解決問題的三條規(guī)則
- 類中的方法優(yōu)先級最高措译。類或父類中聲明的方法的優(yōu)先級高于任何聲明為默認方法的優(yōu)先級。
- 如果無法依據(jù)第一條進行判斷饰序,那么子接口的優(yōu)先級最高:函數(shù)簽名相同時领虹,優(yōu)先選擇擁有最具體實現(xiàn)的默認方法的接口,即如果B繼承了A求豫,那么B就比A更加具體塌衰。
- 最后蝠嘉,如果還是無法判斷最疆,繼承了多個接口的類必須通過顯示覆蓋和調(diào)用期望的方法蚤告,顯示的選擇使用哪一個默認方法的實現(xiàn)。
以上三條規(guī)則就能夠處理所有沖突的問題杜恰,下面舉個例子說明一下获诈。
示例一
public interface A {
default void hello() {
System.out.println("A");
};
}
public interface B extends A{
@Override
default void hello() {
System.out.println("B");
};
}
public class D implements A {
//未實現(xiàn)hello()
}
public class C extends D implements B, A {
public static void main(String... args) {
new C().hello();
}
}
看上面代碼,首先根據(jù)規(guī)則1心褐,類中聲明的方法具有最高權(quán)限烙荷,但是類D中并未實現(xiàn)默認方法hello檬寂。這里如果類D實現(xiàn)了hello,那么他就擁有最高優(yōu)先級了。接下來看規(guī)則2昼伴,看誰更加具體匾旭,因為B繼承了A,那么B更加具體圃郊,所以打印出來的是"B"价涝。
示例二
先上代碼
public interface A {
default void hello() {
System.out.println("A");
};
}
public interface B {
default void hello() {
System.out.println("B");
};
}
public class C implements B, A {
public static void main(String... args) {
new C().hello();
}
}
這種情況會打印什么呢?答案是會拋出編譯錯誤色瘩,因為無法判斷出誰更具體一些逸寓。這個時候就需要顯式調(diào)用了居兆。
具體的方法是覆蓋實現(xiàn)該方法竹伸,并且在方法中顯式調(diào)用。關(guān)于顯示調(diào)用勋篓,Java8中引入的新語法X.super.m(...),其中X是你希望調(diào)用的m方法所在的父接口譬嚣。例如钢颂,上面的問題就可以像下面代碼這樣顯示調(diào)用拜银。
public class C implements B,A {
public static void main(String... args) {
new C().hello();
}
@Override
public void hello() {
A.super.hello();
}
}
最后
多說兩句
雖然現(xiàn)在接口里也可以寫實現(xiàn)了,抽象類和接口還是有區(qū)別的盐股。首先類只能繼承一個抽象類,但可以實現(xiàn)多個接口疯汁。其次抽象類可以定義變量,接口不行幌蚊。
個人認為默認函數(shù)最大的意義還是便于API的編寫,避免接口必須實現(xiàn)的痛苦溢豆。
最最后
打完,收工搓茬!不要問我為什么跳過了Stream犹赖,因為功能很強大卷仑,寫起來太費勁了。歡迎大家討論提出意見~~
歡迎關(guān)注【Funny新青年】微信公眾號