1),接口是一種規范,就是告訴外界這個東東可以做什么。
2),抽象類是一種模板,就是告訴外界這個東西的一部分公共功能。
3),類和上面的接口,抽象類最大的區別就是類可以維護狀態信息。
- 在以前的接口中
2,接口只能繼承接口,不能繼承類
3,接口里沒有普通方法,方法全是抽象的
4,接口里的方法默認修飾符是public abstract
5,接口里的字段全是全局常量,默認修飾符是public static final
java8中的接口在原來的基礎上增加了一些功能,主要是2個:默認方法,靜態方法。
- 1,默認方法
for(int i = 0; i<list.size();i++) { System.out.println(list.get(i)); }
現在我們使用函數函數式編程,代碼如下:
public static void main(String[] args) { List<Integer> list = Lists.newArrayList(1, 2, 3); Stream<Integer> stream = list.stream(); stream.forEach(System.out::println); }現在假設我們自己不用流,我們想直接在list集合上面提供一個forEach方法,用來將一個函數應用到集合的每一個元素上面。下面是使用這種方式編寫的代碼:
list.forEach(System.out::println);上面的效果很好,如果我們自己定義也就是說重新設計集合當然是沒有問題的,但是java的集合是許多年前設計的,這就會帶來一個問題,我們為List接口增加了一個方法,那么所有的實現類都要改過,這樣子代碼才不會報錯,這基本是無法想象的。所以呢,java設計者希望通過允許接口包含具體實現的方法來一勞永逸的解決這個問題。當然,為接口提供默認方法還有別的好處,我們一會會整理到。
java8中允許在接口中定義默認方法,默認方法必須使用default來修飾。先來看一個例子,熟悉下java8中默認方法的語法:
public class Test implements TestInterface { public static void main(String[] args) { Test test = new Test(); //使用自己類中的方法 System.out.println(test.getId()); //使用接口中的默認方法 System.out.println(test.getName()); } @Override public Integer getId() { return 25; } } interface TestInterface { Integer getId(); default String getName() { return "默認方法里面的輸出。。。"; } }
上面的代碼使用了TestInterface接口中定義的默認方法getName(),程序執行沒一點問題。當然我們也可以自己來重寫接口中默認方法中的默認方法。
public class Test implements TestInterface { public static void main(String[] args) { Test test = new Test(); //使用自己類中的方法 System.out.println(test.getId()); //使用接口中的默認方法 System.out.println(test.getName()); } @Override public Integer getId() { return 25; } @Override public String getName() { //默認使用接口中的默認方法,注意下面的使用父類接口中方法的語法 //return TestInterface.super.getName(); return "自己的類中重寫了接口中的默認方法"; } } interface TestInterface { Integer getId(); default String getName() { return "默認方法里面的輸出。。。"; } }在上面的使用過程中,我們發現了現在使用java8接口的默認方法很好的解決了前面我說的問題了,我們不必在每個實現類中都去實現接口中新加的方法了,只需要在接口中增加一個default修飾的默認方法就好了。
另外,在上面的編碼過程中,我們不難還能發現一個使用默認方法帶來的好處,我們現在可以很方便的在接口中指定本質上可選的方法,根據接口的使用方式來選擇使用的方法。舉個例子吧,比如我們定義了一個接口,里面有一個刪除元素的方法remove(),但是實際情況是這個接口應該支持可修改和不可修改2種系列,在可修改系列中沒有問題,我們直接實現remove()方法就好了,不可修改系列我們不需要這個方法,但是也只能定義一個remove()的空實現,最好拋出一個異常。現在我們使用java8中的默認方法,就避免了用於不可修改系列的類也必須定義自己的,占位符性質的多余的方法,總結一下就是說默認方法讓一些類要去實現的方法變成了可選方法。
- 總結一下:
1,優雅的隨時間演化接口。也就是代碼的向后兼容性
2,提供可選功能,類不必在不需要該功能的時候提供占位符實現。
- 2,多級繼承的問題
不慌的,解決上面的問題很簡單,就是定義一組規則就好了,按照一個約定來定義解決上面的沖突。具體的規則如下:
1),在所有的情況,類實現的優先級高於接口的默認實現,也就是先使用自己類中定義的方法或者是父類中的方法。類優先是解決上面系列問題的最主要的規則。
2),當類同時實現了2個接口,2個接口中如果包含相同的默認方法,那么這個類中就必須重寫這個接口,不然代碼報錯。
3),如果是一個接口繼承了另外一個接口,2個接口中也包含相同的默認方法,那么繼承接口的版本具有更高的優先級。比如A擴展了B接口,那么優先使用A類里面的test方法。
4),通過使用super,可以顯式的引用被繼承接口的默認實現,語法如下:InterfaceName.super.methodName()。
相關代碼如下:
public class Test extends C { public static void main(String[] args) { Test test = new Test(); System.out.println(test.getName()); } } class C implements A, B { @Override public Integer getId() { // TODO Auto-generated method stub return null; } //不管接口A和接口B中的這個方法是不是默認方法,有沒有提供實現都必須重寫, 不然代碼報錯 @Override public String getName() { System.out.println(A.super.getName()); return "自己類中必須重寫這個方法。。。"; } } interface A { Integer getId(); default String getName() { return "接口A。。。"; } } interface B { default String getName() { return "接口B。。。"; } }
- 3,在接口中使用靜態方法
接口名字.靜態方法名。
public class Test implements A { public static void main(String[] args) { System.out.println(A.getName()); } } interface A { static String getName() { return "接口A。。。"; } }
注意,實現接口的類或者子接口不會繼承接口中的靜態方法。static不能和default同時使用。在java8中很多接口中都增加了靜態方法,比如下面代碼:
public class Test { public static void test(List<String> list) { //直接使用Comparator的靜態方法 list.sort(Comparator.comparing(String::length)); } public static void main(String[] args) { List<String> list = Lists.newArrayList("122","2","32"); test(list); for (String str : list) { System.out.println(str); } } }