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



在Java中,当您向接口添加新方法时,您会破坏所有客户端。当你有一个抽象类时,你可以添加一个新方法并在其中提供一个默认实现。所有客户端都将继续工作。

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

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

我非常感谢你的评论。

我可以看到

  • 您假设客户端将使用一个新的重载方法,但他们没有使用,因为代码尚未重新编译
  • 您添加了一个客户端已经拥有的方法,该方法可以执行不同的操作
  • 您添加了一个方法,这意味着它们的子类在重新编译时会中断。IMHO这是可取的
  • 您可以更改将在运行时导致错误的返回类型、方法名或参数类型
  • 可以交换相同类型的参数。这可能是最糟糕、最微妙的错误。)

IMHO,是细微的问题更容易让你悲伤。然而,我不会认为简单地添加一个方法就会破坏代码,也不会认为如果客户端的代码没有得到运行时错误,就意味着他们使用的是任何东西的最新版本。)


如果我编译这个代码

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()被评论掉的情况下运行,我没有问题。

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

你的建议会让你怀疑你正在处理的对象是否真的有他们试图调用的方法的实现。

当然,在您的场景中,这不会发生在遗留客户端上,直到您想更新它们一段时间,并且您依赖于您的对象实现IDE预览的所有方法。:)

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

希望这有助于澄清问题。

问候

Interface表示该接口中可供用户使用的一组ALL方法,而不是可以添加其他方法的一些方法。这是一份不减不增的契约。

这个设计最棒的地方是没有歧义。Java是一种静态类型的语言,需要完全了解接口声明中所有可用的方法。JVM中只有一个接口类的表示形式,并且需要包含一整套可用的抽象方法。

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

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

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

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

最新更新