Static and Default Methods in Interfaces in Java – Java中接口的静态和默认方法

最后修改: 2017年 12月 27日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Java 8 brought a few brand new features to the table, including lambda expressions, functional interfaces, method references, streams, Optional, and static and default methods in interfaces.

Java 8带来了一些全新的功能,包括lambda表达式功能接口方法引用streamsOptional、以及接口中的staticdefault方法。

We’ve already covered a few of these features in another article. Nonetheless, static and default methods in interfaces deserve a deeper look on their own.

我们已经在另一篇文章中介绍了其中的一些特性。尽管如此,接口中的staticdefault方法还是值得我们单独深入研究。

In this tutorial, we’ll learn how to use static and default methods in interfaces, and discuss some situations where they can be useful.

在本教程中,我们将学习如何在接口中使用staticdefault方法,并讨论它们可能有用的一些情况。

2. Why Interfaces Need Default Methods

2.为什么接口需要默认方法

Like regular interface methods, default methods are implicitly public; there’s no need to specify the public modifier.

与常规的接口方法一样,默认方法是隐式的;不需要指定public修饰符。

Unlike regular interface methods, we declare them with the default keyword at the beginning of the method signature, and they provide an implementation.

与普通的接口方法不同,我们在方法签名的开头用default 关键字来声明它们,并且它们提供了一个实现

Let’s look at a simple example:

我们来看看一个简单的例子。

public interface MyInterface {
    
    // regular interface methods
    
    default void defaultMethod() {
        // default method implementation
    }
}

The reason why the Java 8 release included default methods is pretty obvious.

Java 8版本包含default方法的原因非常明显。

In a typical design based on abstractions, where an interface has one or multiple implementations, if one or more methods are added to the interface, all the implementations will be forced to implement them too. Otherwise, the design will just break down.

在一个典型的基于抽象的设计中,一个接口有一个或多个实现,如果一个或多个方法被添加到接口中,所有的实现也将被迫实现它们。否则,这个设计就会被打破。

Default interface methods are an efficient way to deal with this issue. They allow us to add new methods to an interface that are automatically available in the implementations. Therefore, we don’t need to modify the implementing classes.

默认接口方法是处理这一问题的有效方法。它们允许我们为一个接口添加新的方法,这些方法在实现中自动可用。因此,我们不需要修改实现类。

In this way, backward compatibility is neatly preserved without having to refactor the implementers.

通过这种方式,后向兼容性得到了整齐的保留,而无需重构实现者。

3. Default Interface Methods in Action

3.行动中的默认接口方法

To better understand the functionality of default interface methods, let’s create a simple example.

为了更好地理解default接口方法的功能,我们来创建一个简单的例子。

Suppose we have a naive Vehicle interface and just one implementation. There could be more, but let’s keep it that simple:

假设我们有一个天真的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.";
    }
}

Now let’s write the implementing class:

现在我们来写一下实现类。

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.";
    }
}

Finally, let’s define a typical main class, which creates an instance of Car and calls its methods:

最后,让我们定义一个典型的main类,它创建一个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());
}

Please notice how the default methods, turnAlarmOn() and turnAlarmOff(), from our Vehicle interface are automatically available in the Car class.

请注意我们的Vehicle接口中的default方法,turnAlarmOn()turnAlarmOff()是如何在Car类中自动使用的。

Furthermore, if at some point we decide to add more default methods to the Vehicle interface, the application will still continue working, and we won’t have to force the class to provide implementations for the new methods.

此外,如果在某个时候我们决定为Vehicle接口添加更多的default方法,应用程序仍将继续工作,我们不必强迫该类为新方法提供实现。

The most common use of interface default methods is to incrementally provide additional functionality to a given type without breaking down the implementing classes.

接口默认方法最常见的用途是在不分解实现类的情况下,逐步为某个特定类型提供额外的功能。

In addition, we can use them to provide additional functionality around an existing abstract method:

此外,我们可以使用它们来围绕现有的抽象方法提供额外的功能

public interface Vehicle {
    
    // additional interface methods 
    
    double getSpeed();
    
    default double getSpeedInKMH(double speed) {
       // conversion      
    }
}

4. Multiple Interface Inheritance Rules

4.多个接口的继承规则

Default interface methods are a pretty nice feature, but there are some caveats worth mentioning. Since Java allows classes to implement multiple interfaces, it’s important to know what happens when a class implements several interfaces that define the same default methods.

默认接口方法是一个相当不错的功能,但也有一些值得一提的注意点。由于Java允许类实现多个接口,因此必须了解当一个类实现了多个定义了相同默认方法的接口时会发生什么

To better understand this scenario, let’s define a new Alarm interface and refactor the Car class:

为了更好地理解这种情况,让我们定义一个新的 Alarm接口并重构Car类。

public interface Alarm {

    default String turnAlarmOn() {
        return "Turning the alarm on.";
    }
    
    default String turnAlarmOff() {
        return "Turning the alarm off.";
    }
}

With this new interface defining its own set of default methods, the Car class would implement both Vehicle and Alarm:

由于这个新的接口定义了它自己的default方法集,Car类将同时实现VehicleAlarm

public class Car implements Vehicle, Alarm {
    // ...
}

In this case, the code simply won’t compile, as there’s a conflict caused by multiple interface inheritance (a.k.a the Diamond Problem). The Car class would inherit both sets of default methods. So which ones should we call?

在这种情况下,代码根本无法编译,因为有一个由多个接口继承引起的冲突(又称钻石问题)。汽车类将继承两套默认方法。那么我们应该调用哪一个呢?

To solve this ambiguity, we must explicitly provide an implementation for the methods:

为了解决这种模糊不清的问题,我们必须明确地提供一个方法的实现:

@Override
public String turnAlarmOn() {
    // custom implementation
}
    
@Override
public String turnAlarmOff() {
    // custom implementation
}

We can also have our class use the default methods of one of the interfaces.

我们还可以让我们的类使用其中一个接口的default方法

Let’s see an example that uses the default methods from the Vehicle interface:

让我们看看一个使用default方法的例子,该方法来自Vehicle接口。

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff();
}

Similarly, we can have the class use the default methods defined within the Alarm interface:

同样地,我们可以让该类使用default接口中定义的Alarm方法。

@Override
public String turnAlarmOn() {
    return Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Alarm.super.turnAlarmOff();
}

It’s even possible to make the Car class use both sets of default methods:

甚至可以Car类使用两套默认方法

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}
    
@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}

5. Static Interface Methods

5.静态接口方法

In addition to declaring default methods in interfaces, Java 8 also allows us to define and implement static methods in interfaces.

除了在接口中声明默认方法之外,Java 8还允许我们在接口中定义和实现静态方法

Since static methods don’t belong to a particular object, they’re not part of the API of the classes implementing the interface; therefore, they have to be called by using the interface name preceding the method name.

由于static方法不属于特定的对象,它们不是实现接口的类的API的一部分;因此,它们必须在方法名称前使用接口名称来调用

To understand how static methods work in interfaces, let’s refactor the Vehicle interface and add a static utility method to it:

为了理解static方法如何在接口中工作,让我们重构Vehicle接口并为其添加一个static实用方法。

public interface Vehicle {
    
    // regular / default interface methods
    
    static int getHorsePower(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Defining a static method within an interface is identical to defining one in a class. Moreover, a static method can be invoked within other static and default methods.

在接口中定义static方法与在类中定义static方法是相同的。此外,static方法可以在其它staticdefault方法中被调用。

Let’s suppose that we want to calculate the horsepower of a given vehicle’s engine. We just call the getHorsePower() method:

假设我们想计算某辆车的发动机的马力。我们只需调用getHorsePower()方法。

Vehicle.getHorsePower(2500, 480));

The idea behind static interface methods is to provide a simple mechanism that allows us to increase the degree of cohesion of a design by putting together related methods in one single place without having to create an object.

静态接口方法背后的想法是提供一种简单的机制,使我们能够提高设计的凝聚力,把相关的方法放在一个地方,而不必创建一个对象。

The same can pretty much be done with abstract classes. The main difference is that abstract classes can have constructors, state, and behavior.

抽象类也基本可以做到这一点。主要区别在于,抽象类可以有构造函数、状态和行为

Furthermore, static methods in interfaces make it possible to group related utility methods, without having to create artificial utility classes that are simply placeholders for static methods.

此外,接口中的静态方法使我们有可能将相关的实用方法分组,而不必人为地创建只是静态方法的占位符的实用类。

6. Conclusion

6.结论

In this article, we explored in depth the use of static and default interface methods in Java 8. At first glance, this feature may look a little bit sloppy, particularly from an object-oriented purist perspective. Ideally, interfaces shouldn’t encapsulate behavior, and we should only use them for defining the public API of a certain type.

在这篇文章中,我们深入探讨了Java 8中staticdefault接口方法的使用。乍一看,这个功能可能有点草率,特别是从面向对象的纯粹主义者的角度来看。理想情况下,接口不应该封装行为,我们应该只用它们来定义某种类型的公共API。

When it comes to maintaining backward compatibility with existing code, however, static and default methods are a good trade-off.

然而,当涉及到与现有代码保持向后兼容时,staticdefault方法是一个很好的权衡。

As usual, all the code samples shown in this article are available over on GitHub.

像往常一样,本文中显示的所有代码样本都可以在GitHub上找到