一、简介

在Java接口中引入 默认 方法之后,接口和抽象类之间似乎不再有任何区别了。但事实并非如此——它们之间存在一些根本差异。

在本教程中,我们将仔细研究接口和抽象类,看看它们有何不同。

2. 为什么使用 默认 方法?

默认 方法的目的是在不破坏现有实现的情况下提供外部功能 。引入 默认 方法的最初动机是通过新的 lambda 函数提供对 Collection Framework 的向后兼容性。

3. 带有 默认 方法的接口与抽象类

让我们看一下主要的根本差异。

3.1.状态

抽象类可以有一个状态,并且它的方法可以访问实现的状态 。尽管接口中允许使用 默认 方法,但它们无法访问实现的状态。

我们在 默认 方法中编写的任何逻辑都应该与接口的其他方法相关——这些方法将独立于对象的状态

假设我们创建了一个抽象类 CircleClass ,它包含一个 Stringcolor 来表示 CircleClass 对象的状态:

public abstract class CircleClass {

    private String color;
    private List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    public boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }

    //standard getters and setters
}

在上面的 抽象 类中,我们有一个名为 isValid() 的非抽象方法来根据其状态验证 CircleClass 对象。 isValid() 方法可以访问 CircleClass 对象的状态 并根据允许的颜色验证 CircleClass 的实例。由于这种行为, 我们可以根据对象的状态在抽象类方法中编写任何逻辑

让我们创建一个简单的 CircleClass 实现类

public class ChildCircleClass extends CircleClass {
}

现在,让我们创建一个实例并验证颜色:

CircleClass redCircle = new ChildCircleClass();
redCircle.setColor("RED");
assertTrue(redCircle.isValid());

在这里,我们可以看到,当我们在 CircleClass 对象中放入有效颜色并调用 isValid() 方法时,在内部, isValid() 方法可以访问 CircleClass 对象的状态并检查实例是否包含有效颜色。

让我们尝试使用具有 默认 方法的接口来执行类似的操作:

public interface CircleInterface {
    List<String> allowedColors = Arrays.asList("RED", "GREEN", "BLUE");

    String getColor();
    
    public default boolean isValid() {
        if (allowedColors.contains(getColor())) {
            return true;
        } else {
            return false;
        }
    }
}

众所周知,接口不能有状态,因此 默认 方法无法访问状态。

在这里,我们定义了 getColor() 方法来提供状态信息。子类将重写 getColor() 方法以在运行时提供实例的状态:

public class ChidlCircleInterfaceImpl implements CircleInterface {
    private String color;

    @Override
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

让我们创建一个实例并验证颜色:

ChidlCircleInterfaceImpl redCircleWithoutState = new ChidlCircleInterfaceImpl();
redCircleWithoutState.setColor("RED");
assertTrue(redCircleWithoutState.isValid());

正如我们在这里看到的,我们重写了子类中的 getColor() 方法,以便 默认 方法在运行时验证状态。

3.2.构造函数

抽象类可以有构造函数 ,允许我们在创建时初始化状态 。当然,接口没有构造函数。

3.3.语法差异

此外,语法方面几乎没有差异。 抽象类可以重写 对象 类的方法 ,但接口不能。

抽象类可以使用所有可能的访问修饰符声明实例变量, 并且可以在子类中访问它们。接口只能有 public、 staticFinal 变量,不能有任何实例变量。

此外, 抽象类可以声明实例和静态块 ,而接口不能拥有其中任何一个。

最后, 抽象类不能引用 lambda 表达式 ,而接口可以有一个可以引用 lambda 表达式的抽象方法。

4。结论

本文展示了抽象类和具有 默认 方法的接口之间的区别。我们还根据我们的场景了解了哪一种最适合。

只要有可能,我们应该 始终选择具有 默认 方法的接口,因为它允许我们扩展类并 实现接口

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