在Java8以前,定義在接口中的全部方法都須要在接口實現類中提供一個實現,若是接口的提供者須要升級接口,添加新的方法,那麼全部的實現類都須要把這個新增的方法實現一遍,若是說全部的實現類可以本身控制的話,那麼還能接受,可是現實狀況是實現類可能不受本身控制。好比說Java中的集合框架中的List接口添加一個方法,那麼Apache Commons這種框架就會很難受,必須修改全部實現了List的實現類面試
Adapter
對接口中全部的方法作空實現,在Spring中咱們可看到不少這種例子,好比WebMvcConfigurerAdapter
@Deprecated public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { } @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { } 省略其餘代碼... }
在Java8之後這些類都被標註成了過時@Deprecated
spring
爲了解決上述問題,在Java8中容許指定接口作默認實現,未指定的接口由實現類去實現。如何標識出接口是默認實現呢?方法前面加上default
關鍵字。好比Spring中的WebMvcConfigurer
segmentfault
public interface WebMvcConfigurer { default void configurePathMatch(PathMatchConfigurer configurer) { } default void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } default void configureAsyncSupport(AsyncSupportConfigurer configurer) { } 省略其餘代碼... }
從如今看來,可能你們都會有個疑問,默認方法和抽象類有什麼區別呢?架構
- 默認方法不能有實例變量,抽象類能夠有
- 一個類只能繼承一個抽象類,當時能夠實現多個接口
爲接口提供可選的方法,給出默認實現,這樣實現類就不用顯示的去提供一個空的方法;這種場景剛纔咱們在上面已經說到了,spring中有大量的例子框架
繼承是面向對象的特性之一,在Java中一直以來都是單繼承的原則,Java8中默認方法爲實現多繼承提供了可能(因爲接口中不能有實例對象,因此可以抽象的到接口中的行爲通常都是比較小的模塊);從我的的經從來看,作遊戲是訓練本身面向對象思惟的最好方式(之後有機會分享一下小遊戲的製做),由於如今大部分學Java的同窗學完Java基礎後就直接進入JavaWeb的學習,整合各類框架,只能在通用的三層架構(Controller、Service、Dao)中寫本身的邏輯。ide
相信不少人在都作個坦克大戰的遊戲,若是用Java8中的默認方法如何設計好多繼承呢?
學習
這裏咱們舉個簡單的例子,定義了三個接口:spa
Moveable
:容許移動的物體,把移動的邏輯放入到這個接口中的默認方法Attackable
: 容許攻擊的物體,把攻擊的通用邏輯放入到默認方法,不通用的邏輯經過模板方法給實現類處理Location
: 獲取物體的座標經過這些接口的組合方式,咱們就能夠爲遊戲建立不一樣的實現類,好比說坦克、草地、牆壁...設計
經過上面的例子,咱們體驗的默認方法給咱們帶來了多繼承的便利,可是讓咱們思考下,若是出現了不一樣的類出現的相同簽名的默認方法,實際在運行的時候應該如何選擇呢?客官不慌,有辦法的3d
public class C implements A, B { void pint() { B.supper.print(); // 顯式調用 } }
爲了說明上面的三個原則,咱們直接來看看最複雜的菱形繼承問題
public interface A { default void print(){ System.out.println("Class A"); } } public interface B extend A {} public interface C extend A {} public class D implement B, C { public static void main(String[] args) { new D().print() } }
這種狀況下B,C都沒有本身的實現,實際上就只有A有實現,那麼會打印Class A
若是說這個時候把接口B接口改一下
public interface B extends A { default void print(){ System.out.println("Class B"); } }
根據原則(2),B繼承於A,更加具體,因此打印結果應該是B
若是說把接口C修改一下
public interface C extends A { default void print(){ System.out.println("Class C"); } }
這時候咱們發現編譯報錯,須要咱們本身手動指定,修改D中的代碼
public class D implements B, C { @Override public void print() { C.super.print(); } public static void main(String[] args) { new D().print(); } }
default
來修飾