为什么在Java接口中添加新方法会破坏依赖于旧版本的客户端?

时间:2012-09-24 08:34:11

标签: java design-patterns interface jvm-languages

在Java中,当您向界面添加新方法时,您将破坏所有客户端。如果有抽象类,则可以添加新方法并在其中提供默认实现。所有客户都将继续工作。

我想知道为什么界面是这样设计的?

所有旧方法仍然存在,所以似乎没有向后兼容性问题。 (当然需要有一些例外,但我认为能够在不破坏客户端的情况下向java接口添加新方法可能是个好主意......)

感谢您的评论。

4 个答案:

答案 0 :(得分:3)

我可以看到一些可能的休息

  • 您认为客户端将使用新的重载方法,但它们不会,因为代码尚未重新编译。
  • 你添加了一个客户已经拥有的方法,它做了不同的事情。
  • 你添加一个方法,这意味着他们的子类在重新编译时会中断。恕我直言这是可取的。
  • 您更改将在运行时导致错误的返回类型,方法名称或参数类型。
  • 您交换相同类型的参数。这可能是最糟糕,最微妙的错误。 ;)
恕我直言,这是微妙的问题,更容易引起你的悲伤。但是,我不认为简单地添加方法会破坏代码,我不会假设如果客户端的代码没有获得运行时错误意味着他们使用的是最新版本的任何东西。 ;)


如果我编译此代码

public interface MyInterface {
    void method1();

    // void method2();
}

public class Main implements MyInterface {
    @Override
    public void method1() {
        System.out.println("method1 called");
    }

    public static void main(String... args) {
        new Main().method1();
    }
}

打印

method1 called

然后取消注释method2()并重新编译接口。这意味着接口具有Main未实现的方法。然而,当我在不重新编译Main的情况下运行它时,我得到了

method1 called

如果我有

public class Main implements MyInterface {    
    public void method1() {
        System.out.println("method1 called");
    }

    public void method2() {
        System.out.println("method2 called");
    }

    public static void main(String... args) {
        new Main().method1();
    }
}

我和// method2()一起评论说出来,我没有问题。

答案 1 :(得分:1)

接口就像一个类的模板。如果有一个对象,其类实现某个接口并且您对该接口进行强制转换,则只能通过该接口访问该对象(及其方法)。因此,您的客户端将始终看到接口提供的所有方法,而不仅仅是那些实际上由类实现的方法。

您的建议会导致您想知道您在任何时刻处理的对象是否确实具有他们尝试调用的方法的实现。

当然,对于遗留客户端来说,这种情况不会发生,直到您想要更新它们一段时间,并且您依赖于具有IDE预览的所有方法的实现的对象。 :)

使用抽象类的事实(正如您所提到的)您提供了一个默认实现,因此,在客户端,可以依赖于实现方法的对象。

希望这有助于澄清事情。

此致

答案 2 :(得分:0)

Interface表示从该接口可用于其用户的一组 ALL 方法,而不是可以添加其他方法的某些方法。这是一份不少也不多的合同。

这个设计的最好的事情是没有歧义。 Java是一种静态类型语言,需要完全了解接口声明中所有可用的方法。在JVM中,只有一个表示的接口类,它需要包含完整的抽象方法集。

当你有一个带有一些实现方法的抽象类时,它的语义是一个无法实例化但其唯一目的是扩展并实现其抽象方法的类。 抽象类强制执行IS-A关系,而接口强制执行BEHAVES-AS

答案 3 :(得分:0)

接口的作用不仅仅是方法的声明。它们构成了基于合同/第一次开发的基础。这些契约apis定义了什么作为输入和什么作为输出。和任何合同一样,只有满足所有条件时它们才有效。这就是一个类必须实现所实现接口的所有apis的原因。

有设计模式定义了处理版本和新开发的方式。工厂/抽象工厂模式应该在实例化这些接口的实现时使用(这样他们可以在内部验证正确的版本是要使用的实现)。

您要求的灵活性可以通过适当的设计来实现,而不是期望它来自编程语言。对于将新版本实现插入旧接口的编程工具,编程错误。但它与向后兼容性中断不同。 较新版本组件的可用性并不一定意味着会出现向后兼容性中断。成熟的产品或组件始终支持旧版本,只要它们不会成为维护的真正痛苦。因此,在大多数情况下,除非您希望使用某些新功能,否则用户不必担心新版本的可用性。