Java默认接口方法引发的多继承问题
Java8中的抽象类和抽象接口区别
首先,一个类只能继承一个抽象类,但是一个类可以实现多个接口
其次,一个抽象类可以通过实例变量(字段)保存一个通用状态,而接ロ是不能有实例变量的
熟悉Java或者看过Java面试题的都知道,Java不支持多继承,原因是:
- 安全性的考虑,如果子类继承的多个父类里面有相同的方法或者属性,子类将不知道具体要继承哪个。
- Java提供了接口和内部类以达到实现多继承功能,弥补单继承的缺陷。
所以在实际开发中可以通过多个接口来灵活实现,但Java8中支持接口编写默认方法,变相等于可以继承多个类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
interface A{ default void a(){ System.out.println("a"); } } abstract class B{ public void b(){ System.out.println("b"); } } class C extends B implements A{ public void c(){ System.out.println("c"); } } |
Java8的这种改进源于接口升级迭代存在的问题,一旦在接口中新加入方法,异味着所有实现该接口的类都要实现该方法,例如List接口增加新的方法, ArrayList ,List, LinkedList, Vector以及所有相关的类都要发生变化,这简直就是灾难,对整个项目会产生非常大的影响,所以我理解Java开发者为了加入新的功能而又不影响现有代码的运行,容许接口中来编写默认方法,默认方法在Java8的API中已经大量地使用了。如大量使用的co11ection接口的 stream方法就是默认方法。list接口的sort方法也是默认方法。很多函数式接口,比如 Predicate、 Functionl以及 Comparator也引人了新的默认方法,比如 Predicate.and或者 Function. andthen,list新加入的sort排序。
1 2 3 4 5 6 7 8 9 10 |
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } } |
由于接口现在可以提供带实现的方法,这意味着Java已经在某种程度上实现了多继承,如果实现类也实现了同样的方法默认方法会被覆盖吗,Java8中已经定义了一些规则和机制来处理这些问题。
如果一个类使用相同的函数签名从多个地方(比如另一个类或接口)继承了方法,通过三条规则可以进行判断。
(1)类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
(2)如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。
(3)最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法。