java 8 接口中缺省方法与静态方法
java 8 新增了很多新的特性,包括lambda表达式,函数接口,方法引用,流,Optional 以及接口中的静态方法与缺省方法。
本文我们深入讨论如何使用接口中的static和default方法,并通过示例展示其应用场景。
为什么需要缺省方法
与正常接口方法一样,default方法默认为public,无需显示指定public修饰符。与正常方法不同,在方法声明之前加上default关键字,同时提供实现。请看示例:
public interface MyInterface {
// regular interface methods
default void defaultMethod() {
// default method implementation
}
}
java 8 使用缺省方法的原因非常明显:
典型基于抽象的设计,接口拥有一个或多个实现。如果在接口中增加方法,所有实现必须也要增加该方法实现,否则无法编译。
缺省方法有效地解决了该问题。其允许在接口中增加新的方法,并自动在接口实现中可用,因此无需修改实现类。通过这种方式,无需重构之前的实现,优雅地实现了向后兼容。
缺省方法示例
为了更好理解缺省方法功能,下面通过示例实战说明。
假设有一个接口Vehicle 及其实现,为了保持示例简单,不再扩展其他内容。
public interface Vehicle {
String getBrand();
String speedUp();
String slowDown();
default String turnAlarmOn() {
return "Turning the vehicle alarm on.";
}
default String turnAlarmOff() {
return "Turning the vehicle alarm off.";
}
}
下面定义其实现类:
public class Car implements Vehicle {
private String brand;
// constructors/getters
@Override
public String getBrand() {
return brand;
}
@Override
public String speedUp() {
return "The car is speeding up.";
}
@Override
public String slowDown() {
return "The car is slowing down.";
}
}
最后,我们定义主类,在里面创建Car实例并调用其方法:
public static void main(String[] args) {
Vehicle car = new Car("BMW");
System.out.println(car.getBrand());
System.out.println(car.speedUp());
System.out.println(car.slowDown());
System.out.println(car.turnAlarmOn());
System.out.println(car.turnAlarmOff());
}
注意,Vehicle接口中的缺省方法turnAlarmOn() 和 turnAlarmOff() 在Car类中自动可用。而且,如果后期在Vehicle接口中增加缺省方法,应用仍然可以正常运行,不是必须提供新增方法的实现。
在接口中使用缺省方法的典型场景是————为给定类型提供额外功能,无需修改其实现类。另外,也可以用于给现有的抽象方法提供实现:
public interface Vehicle {
// additional interface methods
double getSpeed();
default double getSpeedInKMH(double speed) {
// conversion
}
}
多接口继承规则
缺省方法确实是非常好的特性,但有些方面需要提醒。java允许类实现多个接口,当一个类实现多个接口时,接口中定义了相同的缺省方法会怎么?
为了更好理解,我们定义新的Alarm 接口并重构Car类:
public interface Alarm {
default String turnAlarmOn() {
return "Turning the alarm on.";
}
default String turnAlarmOff() {
return "Turning the alarm off.";
}
}
该接口定义了一组缺省方法,Car类实现Vehicle和Alarm两个接口:
public class Car implements Vehicle, Alarm {
// ...
}
这种情况下,代码不能编译,因为多接口继续引起冲突————菱形问题。Car类继承两组缺省方法,其不知道调用哪一个。
为了解决这种歧义问题,我们需要提供该方法的显示实现:
@Override
public String turnAlarmOn() {
// custom implementation
}
@Override
public String turnAlarmOff() {
// custom implementation
}
我们也可以在类中使用其中一个接口的缺省方法,下面看示例使用Vehicle接口:
@Override
public String turnAlarmOn() {
return Vehicle.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Vehicle.super.turnAlarmOff();
}
类似的,也可以使用Alarm接口的缺省方法:
@Override
public String turnAlarmOn() {
return Alarm.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Alarm.super.turnAlarmOff();
}
甚至Car类可以使用两个接口的缺省方法:
@Override
public String turnAlarmOn() {
return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}
@Override
public String turnAlarmOff() {
return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}
静态接口方法
除了在接口中声明缺省方法,java8也允许我们在接口中定义static方法实现。static方法不属于特定对象,不属于实现接口的类API的一部分,只能通过接口名称直接调用。
为了理解static方法,我们重构Vehicle接口并增加static方法:
public interface Vehicle {
// regular / default interface methods
static int getHorsePower(int rpm, int torque) {
return (rpm * torque) / 5252;
}
}
在接口中定义static方法与在类中定义static方法一样,而且也可以在其他static方法和default方法中调用它们。假如,我们需要在计算给定类型计算其马力,仅需要调用getHorsePower方:
Vehicle.getHorsePower(2500, 480));
静态接口方法背后的思想是提供一种简单的机制,允许将相关的方法放在一起,而不必创建对象,从而提高设计的内聚性。
抽象类也可以实现同样功能,主要差异是抽象类可以有构造函数、状态以及行为。静态方法可以实现对相关功能方法进行分组,无需在其实现类中定义静态方法占位符。
总结
本文我们深入讨论java 8 中如何使用接口中的static和default方法。乍一看,这个特性可能看起来有些草率,特别是从纯粹面向对象主义者的角度来看。理想情况下,接口不应该封装行为,而只应该定义某种类型的公共API。
但是,在维护与现有代码的向后兼容性方面,静态和默认方法是一种很好的折衷。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/85079730