接口默认方法的影子化

7
考虑以下情况,
interface IFace1 {
    default void printHello() {
        System.out.println("IFace1");
    }
}

interface IFace2 {
    void printHello();
} 

public class Test implements IFace1, IFace2 {

    public static void main(String[] args) {
        Test test = new Test();
        test.printHello();

        IFace1 iface1 = new Test();
        iface1.printHello();

        IFace2 iface2 = new Test();
        iface2.printHello();
    }

    @Override
    public void printHello() {
        System.out.println("Test");
    }
}

在上面的例子中,我得到了以下输出,这是相当预期的。
Test
Test
Test

我一直在阅读有关 Java-8 默认方法的内容,尤其是关于扩展包含默认方法的接口的内容。

第二个项目:重新声明默认方法,使其成为抽象方法。

在上面的例子中,我有两个接口,它们都有相同名称的默认方法,当我实现它们时,我只能到达 TestprintHello 的实现,它引用了 IFace2

我有几个问题:

  1. 我如何才能到达 IFace1printHello 方法?如果我不能,那为什么?
  2. 这种行为难道不会让我远离 IFace1 的本意吗?它现在可能被其他方法所掩盖了吗?

引用说,你可以在子接口中将default方法设置为abstract例如,
interface IFace2 extends IFace1 {
    void printHello();
}

当我实现IFace2时,我将无法实际访问IFace1default方法,这正是我目前遇到的情况。

2个回答

7
看起来你对default方法的存在有些困惑。那么让我们暂时忘记IFace1.printHello()是一个default方法吧。那么,现在情况很清晰:Test实现了两个接口,即IFace1和IFace2,它们恰好都有一个名称和签名相同的方法。
Test实现这个方法,因此实现了这两个接口的方法。新特性default方法不会改变这种逻辑。此外,语言设计者考虑到添加default方法不会影响现有代码的行为,因此如果您的类实现了该方法,default方法的存在就变得无关紧要了。
但是,如果您编写的代码意识到default方法的存在,您可以调用它,如果它被直接超级接口声明或继承,即您的代码可以使用IFace1.super.printHello()来调用IFace1default方法。
规则与超级类的规则并没有太大区别。如果更改接口以使接口IFace2扩展IFace1并仍将printHello()声明为abstract方法,则此abstract方法将覆盖default方法,您将无法从Test中使用IFace1.super.printHello()调用它。
正如所说,这些规则与普通实例方法的规则并没有太大区别。如果Test声明了一个方法printHello(),那么这是您可以通过Test实例的引用调用的唯一方法,无论它的声明类型是TestIFace1还是IFace2。只有Test本身的实现方法才能进行super调用。
当涉及到可能有多个接口继承时,主要区别就出现了。如果您的类Test没有实现方法printHello(),则取决于两个接口的继承关系会发生什么。
  • 如果IFace2扩展了IFace1,它的抽象方法重新声明了default方法,因此编译器会出现一个错误,因为Test必须实现这个abstract方法
  • 如果IFace2没有扩展IFace1,则有两个具有相同名称和签名的可继承方法,因此Test将不会继承default方法,编译器会出错,因为Test必须实现abstract方法
  • 如果IFace1扩展了IFace2Test将继承default方法。如果Test没有实现IFace2,它也会继承该方法,但是这可能会令人惊讶...

5
我该如何调用实现了IFace1接口的printHello方法?如果不能,为什么? 你只能在实现了IFace1接口的类型的实例方法中这样做。
IFace1.super.printHello(); // only if IFace1 declares a default implementation of the method

换句话说,你无法通过简单地引用类型为IFace1Test(或其他)来执行此操作。那样会破坏封装性。

这种行为是否让我远离了IFace1的预期本质,因为现在可能被其他方法掩盖了?

这里没有任何遮蔽。你已经重写了该方法,因此会调用重写后的方法。
关于你的第三个问题,你没有扩展任何接口,所以我看不到相关性。
如果你真的有
interface IFace2 extends IFace1 {
    void printHello();
}

使用

public class Test implements IFace2 {

如果这样做,您将无法访问IFace1方法的default实现。您不能跳过超类型以访问继承层次结构中更高层次的实现。


2
@任务:无论您如何定义接口或者Test是否实现它们,new Test().printHello()都将始终调用在Test中声明的方法。 - Holger
1
@任务 我不明白你的困惑。为什么你期望除了被覆盖的方法之外的其他东西被调用呢? - Sotirios Delimanolis
3
“meaningful” 是什么意思?如果您没有在“Test”中重写它,您的代码将无法编译。 - Sotirios Delimanolis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接