前面介紹了接口的基本用法,有心的朋友可能注意到這麼一句話「在Java8之前,接口內部的全部方法都必須是抽象方法」,如此說來,在Java8以後,接口的內部方法也可能不是抽象方法了嗎?之因此Java8對接口的定義規則發生變化,是由於原來的接口定義存在先天不足致使的,例以下列幾點需求就難以知足:
一、Java8之前規定接口的內部方法只能是抽象方法,在該接口的實現類裏面所有都要重寫。這個規定明顯太霸道了,爲何非得全部都重寫呢?有的行爲分明是通用的,好比呼吸動做,凡是陸上動物都用鼻子呼吸,把新鮮空氣吸進去,再把循環後的空氣呼出來,這個呼吸方法理應放之四海而皆準,根本無需在每一個實現類中依次重寫過去。
二、Java8之前的接口不支持構造方法也就算了,但是它竟然也不支持靜態成員(包括靜態屬性和靜態方法)!這下可苦了程序員,由於與行爲有關的常量與工具方法不能放到接口內部,只能另外寫個工具類填入這些常量與工具方法,因而本來應當在一個屋檐之下的行爲動做和行爲概念不得不分居兩地了。
有鑑於此,從Java8開始,接口順應時代要求進行了規則修訂,針對以上的兩點需求分別補充了相應的處理對策:
一、增長了默認方法,並經過前綴default來標識。接口內部須要編寫默認方法的完整實現代碼,這樣實現類無需重寫該方法便可直接繼承並使用,彷彿默認方法就是父類方法同樣,惟一的區別在於實現類不容許重寫默認方法。
二、增長了靜態屬性和靜態方法,並且都經過前綴static來標識。接口的靜態屬性同時也是終態屬性,初始化賦值以後便沒法再次修改;接口的靜態方法不能被實現類繼承,於是實現類容許定義同名的靜態方法,緣於接口的靜態方法與實現類的靜態方法沒有任何關聯,僅僅是它倆剛好同名而已。
據此對先前的行爲接口Behavior進行加強,按照Java8的新特性補充了默認方法與靜態方法,修補以後的新接口ExpandBehavior代碼以下所示:html
//定義一個增長了Java8新特性的接口 public interface ExpandBehavior { // 聲明瞭一個抽象的飛翔方法 public void fly(); // 聲明瞭一個抽象的游泳方法 public void swim(); // 聲明瞭一個抽象的奔跑方法 public void run(); // 默認方法,之前綴default標識。默認方法不支持重寫,但能夠被繼承。 public default String getOrigin(String place, String name, String ancestor) { return String.format("%s%s的祖先是%s。", place, name, ancestor); } public static int MALE = 0; public static int FEMALE = 1; // 接口內部的靜態屬性也默認爲終態屬性,因此final前綴可加可不加 //public final static int MALE = 0; //public final static int FEMALE = 1; // 靜態方法,以關鍵字static標識。靜態方法支持重寫,但不能被繼承。 public static String getNameByLeg(int leg_count) { if (leg_count == 2) { return "二足動物"; } else if (leg_count == 4) { return "四足動物"; } else if (leg_count >= 6) { return "多足動物"; } else { return "奇異動物"; } } }
根據上面的擴展接口,從新編寫實現了該接口的鵝類,其中fly、swim、run這三個抽象方法均須重寫,惟有默認方法getOrigin不要重寫,而且鵝類代碼當中能夠直接調用這個默認方法。新寫的鵝類代碼ExpandGoose示例以下:java
//定義實現了擴展接口的鵝類 public class ExpandGoose extends Bird implements ExpandBehavior { public ExpandGoose(String name, int sexType) { super(name, sexType); } // 實現了接口的fly方法 @Override public void fly() { System.out.println("鵝飛不高,也飛不遠。"); } // 實現了接口的swim方法 @Override public void swim() { System.out.println("鵝,鵝,鵝,曲項向天歌。白毛浮綠水,紅掌撥清波。"); } // 實現了接口的run方法 @Override public void run() { System.out.println("檻外蕭聲輕盪漾,沙間鵝步滿蹣跚。"); } // 根據產地和祖先拼接並打印該動物的描述文字 public void show(String place, String ancestor) { // getOrigin是來自擴展接口ExpandBehavior的默認方法,能夠在實現類中直接使用 String desc = getOrigin(place, getName(), ancestor); System.out.println(desc); } }
接着輪到外部訪問這個鵝類ExpandGoose了,表面上外部仍跟日常同樣調用鵝類的成員方法,然而在調用接口的靜態成員時有所差異。對於接口的靜態屬性,外部依然可以經過鵝類直接訪問,訪問格式形如「實現類的名稱.靜態屬性名」;對於接口的靜態方法,外部卻不能經過鵝類訪問了,由於實現類並未繼承接口的靜態方法,因此外部只能經過接口自身訪問它的靜態方法,訪問格式形如「擴展接口的名稱.靜態方法名(***)」。下面是外部調用鵝類ExpandGoose的代碼例子:程序員
// 演示擴展接口的實現類用法 private static void testExpand() { // 實現類能夠繼承接口的靜態屬性 ExpandGoose goose = new ExpandGoose("鵝", ExpandGoose.FEMALE); goose.show("中國", "鴻雁"); goose.show("歐洲", "灰雁"); // 接口中的靜態方法沒有被實現類所繼承,於是只能經過擴展接口自身訪問 String typeName = ExpandBehavior.getNameByLeg(2); System.out.println("鵝是"+typeName); }
運行上面的測試代碼,觀察到以下的日誌結果,可見不論是默認方法getOrigin,仍是靜態方法getNameByLeg,都獲得了正確執行:ide
中國鵝的祖先是鴻雁。 歐洲鵝的祖先是灰雁。 鵝是二足動物
更多Java技術文章參見《Java開發筆記(序)章節目錄》工具