什么是默认方法
Java8 中的默认方法是针对接口添加的新特性,它是指接口可以直接对方法进行实现,实现方式很简单,直接在方法定义处添加 default 关键字即可,如下所示。
public interface MyInterface { default void test(){ System.out.println("这是一个默认方法..."); }}
虽然接口中可以直接定义方法,但是调用该方法还是需要实例化对象,所以仍然需要创建接口的实现类,区别在于实现类不必对默认方法进行具体实现,如下所示。
public class MyImplements implements MyInterface {}
根据实现类创建接口的实例化对象,调用默认方法,如下所示。
public class Test { public static void main(String[] args) { MyInterface myInterface = new MyImplements(); myInterface.test(); }}
运行结果如下图所示。
同时我们也可以在实现类中对方法进行实现,从而覆盖接口中的默认方法,如下所示。
public class MyImplements implements MyInterface { @Override public void test() { System.out.println("这是实现类的方法..."); }}
再次运行测试方法,结果如下图所示。
当然也可以通过匿名类的方式对默认方法进行覆盖,如下所示。
public class Test { public static void main(String[] args) { MyInterface myInterface = new MyInterface() { @Override public void test() { System.out.println("这是匿名类方法..."); } }; myInterface.test(); }}
运行结果如下图所示。
了解完默认方法的概念和使用,接下来我们说一说为什么要使用默认方法。
面向接口编程可以实现代码的解耦合,从而提高程序的扩展性,但是这种方式也存在明显的不足,当对接口进行修改时,所有的实现类都要同步修改,这样就非常麻烦,使用默认方法可以解决这一问题,只需要修改接口的默认方法即可,实现类不需要进行处理。
默认方法在 Java8 中的实际应用
Java8 针对集合操作提供了一个新的方法 forEach,并将其定义在 Iterable 接口中,如果没有默认方法机制,那么 Java8 中所有的 Iterable 实现类都需要修改,接口和实现类高度耦合,默认方法可以完美地解决这一问题,只需要将 forEach 定义成默认方法即可,源码如下图所示。
默认方法的继承
默认方法支持继承,即子接口可以继承父接口中的默认方法,如下所示。
/** * 父接口 */public interface MyInterface { default void test(){ System.out.println("这是一个默认方法..."); }}/** * 子接口 */public interface MyInterfaceB extends MyInterface {}/** * 实现类 */public class MyImplements implements MyInterfaceB {}/** * 测试类 */public class Test { public static void main(String[] args) { MyInterfaceB myInterfaceB = new MyImplements(); myInterfaceB.test(); }}
运行结果如下图所示。
同时子接口也可以完成对父接口默认方法的重写,如下所示。
/** * 父接口 */public interface MyInterface { default void test(){ System.out.println("这是一个默认方法..."); }}/** * 子接口 */public interface MyInterfaceB extends MyInterface { @Override default void test() { System.out.println("这是子接口的默认方法..."); }}/** * 实现类 */public class MyImplements implements MyInterfaceB {}/** * 测试类 */public class Test { public static void main(String[] args) { MyInterfaceB myInterfaceB = new MyImplements(); myInterfaceB.test(); }}
运行结果如下图所示。
接口多实现场景下默认方法冲突的解决方案
如果两个不同的接口中定义同名的默认方法,创建一个实现类来同时实现这两个接口,就会出现问题,代码如下所示。
/** * 接口1 */public interface Run { default void test(){ System.out.println("跑起来了..."); }}/** * 接口2 */public interface Fly { default void test(){ System.out.println("飞起来了..."); }}public class MyImplements implements Run,Fly {}
异常信息如下图所示。
MyImplements 无法区分两个默认方法,此时需要在实现类中通过 super 关键字来指定调用哪个接口的默认方法,如下所示。
public class MyImplements implements Run,Fly { public void test(){ Fly.super.test(); }}
同时也可以在实现类中对方法进行重新定义,覆盖接口的默认方法,如下所示。
public class MyImplements implements Run,Fly { public void test(){ System.out.println("MyImplements..."); }}
静态默认方法
默认方法也可以定义为静态,直接通过接口调用,具体操作如下所示。
public interface MyInterface { static void staticTest(){ System.out.println("这是一个静态默认方法..."); }}public class Test { public static void main(String[] args) { MyInterface.staticTest(); }}
运行结果如下图所示。