java 8 接口中缺省方法与静态方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/neweastsun/article/details/85079730

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。
但是,在维护与现有代码的向后兼容性方面,静态和默认方法是一种很好的折衷。

猜你喜欢

转载自blog.csdn.net/neweastsun/article/details/85079730