Java 中的方法分派:为什么它不能在运行时区分接口和类参数?

mar*_*odb -2 java oop overloading java-17

我正在开发一个具有接口I和类AB实现它的 Java 程序。我还有另一个类实现C ,它有 2 个带有一个参数的方法:一个接受 A 的对象,另一个接受 B 的对象。

  interface I {}
  static class A implements I {}
  static class B implements I {}
  interface C {
    String map(A a);
    String map(B b);
  }

  public static void main(String[] args) {
    // Injected at runtime;
    C mapper = getInstance();
    I a = new A();
    I b = new B();
    // This fail at compilation 
    String result = mapper.map(a);
  }   
private static C getInstance() {
    return new C() {
      @Override
      public String map(A a) {
        return "a";
      }
      @Override
      public String map(B b) {
        return "b";
      }
    };
  }
Run Code Online (Sandbox Code Playgroud)

当我尝试使用 A 或 BI 的对象调用 C 的方法时,出现以下错误:

java: no suitable method found for map(Main.I)
    method Main.C.map(Main.A) is not applicable
      (argument mismatch; Main.I cannot be converted to Main.A)
    method Main.C.map(Main.B) is not applicable
      (argument mismatch; Main.I cannot be converted to Main.B)
Run Code Online (Sandbox Code Playgroud)

即使我们说类型的其他实例I可以传递给映射器,这也应该通过以下方式修复:sealed interface I permits A, B {}

为什么Java中没有运行时方法调度? 我的问题是为什么在java中它不可能比“为什么它不起作用”更多

Bri*_*etz 6

对于“因为重载选择是在编译时基于参数的静态类型完成的,而不是在运行时基于参数的动态类型完成的”的效果有多种答案。这些陈述 100% 正确;这就是重载的工作原理以及它的设计原理。当 Java 选择单一(基于接收器)调度时,多重调度(“多方法”)是众所周知的。

下一个合理的问题是为什么要这样做?1995 年的成功有以下几个原因:

  • 可预测性。 如果给定的调用站点始终对应于完全相同的(虚拟)方法,则这是一个简单的好处。
  • 安全。 在某些情况下,不存在“最佳”过载。在编译时确定这一点并提供明确的错误消息,比仅在传递意外的输入组合时才让程序在运行时失败要好。让每个方法分派都可能抛出“没有最具体的重载”异常会降低语言的可靠性。
  • 表现。 重载选择的成本可能很高;最好在编译时预先执行此操作,而不会给运行时带来负担。
  • 简单。 重载和覆盖的交互将比现在复杂得多,因为,例如,我们需要更多规则来打破“超类中更具体的方法和子类中被覆盖的候选方法”之间的联系。这将导致更令人惊讶的互动。
  • 实用性。 真正的多重调度很少见;采用多参数调度可能会使语言和运行时变得复杂,超出其表达价值。

到 2023 年,编译器和运行时技术已经有了相当大的改进,因此编译时重载选择和运行时分派之间的平衡可能会发生一些变化,但总的来说,这仍然是一个明智且务实的权衡。