在 Java 中,当您向接口添加新方法时,您会破坏所有客户端。当您有一个抽象类时,您可以添加一个新方法并在其中提供一个默认实现。所有客户将继续工作。
我想知道为什么界面是这样设计的?
所有旧方法仍然存在,因此似乎没有向后兼容性问题。(当然需要有某些例外,但我认为能够在不破坏客户端的情况下向 java 接口添加新方法可能是一个非常好的主意......)
我会很感激你的意见。
在 Java 中,当您向接口添加新方法时,您会破坏所有客户端。当您有一个抽象类时,您可以添加一个新方法并在其中提供一个默认实现。所有客户将继续工作。
我想知道为什么界面是这样设计的?
所有旧方法仍然存在,因此似乎没有向后兼容性问题。(当然需要有某些例外,但我认为能够在不破坏客户端的情况下向 java 接口添加新方法可能是一个非常好的主意......)
我会很感激你的意见。
我可以看到一些可能的中断
恕我直言,这是更可能导致您悲伤的微妙问题。但是,我不会假设简单地添加一个方法会破坏代码,我不会假设如果客户端的代码没有出现运行时错误,则意味着他们使用的是最新版本的任何东西。;)
如果我编译这段代码
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 预览的所有方法都有实现。:)
抽象类的事实是(正如您所提到的)您提供了默认实现,因此可以在客户端依赖您的对象实现方法。
希望这有助于解决问题。
问候
接口表示从该接口对其用户可用的一组所有方法,而不是可以添加其他方法的一些方法。这是一份不少也不多的契约。
这种设计最好的一点是没有歧义。Java 是一种静态类型语言,需要从接口声明中完全了解所有可用方法。在 JVM 内部,只有一个接口类表示,它需要包含完整的可用抽象方法集。
当您有一个具有一些已实现方法的抽象类时,它的语义是一个无法实例化但其唯一目的是扩展并实现其抽象方法的类。抽象类强制执行 IS-A 关系,而接口强制执行 BEHAVES-AS
接口的作用不仅仅是方法的声明。它们构成了基于合同/首次开发的基础。这些合约 API 定义了什么是输入,什么是输出。和任何合同一样,它们只有在满足所有条件时才有效。这就是类必须实现已实现接口的所有api的原因。
有一些设计模式定义了处理版本和新开发的方式。在实例化这些接口的实现时应该使用的工厂/抽象工厂模式(以便它们可以在内部验证要使用的正确版本是实现)。
您所要求的灵活性可以通过适当的设计来实现,而不是期望通过编程语言来实现。对于将新版本实现插入旧接口的编程工具是编程错误。但它不同于向后兼容中断。更新版本的组件的可用性并不一定意味着会有向后兼容性中断。成熟的产品或组件总是支持旧版本,只要它们不会成为真正的维护难题。因此,在大多数情况下,除非有一些您想使用的新功能,否则用户不必担心更新版本的可用性。