Java 中的重载和多重调度

2022-09-01 12:58:41

我有一个集合(或列表或数组列表),我想在其中放置字符串值和双精度值。我决定让它成为对象的集合,并使用重载 ond 多态性,但我做错了什么。

我运行了一个小测试:

public class OOP {
    void prova(Object o){
        System.out.println("object");
    }

    void prova(Integer i){
    System.out.println("integer");
    }

    void prova(String s){
        System.out.println("string");
    }

    void test(){
        Object o = new String("  ");
        this.prova(o); // Prints 'object'!!! Why?!?!?
    }

    public static void main(String[] args) {
        OOP oop = new OOP();
        oop.test(); // Prints 'object'!!! Why?!?!?
    }
}

在测试中,参数类型似乎是在编译时决定的,而不是在运行时决定的。为什么?

这个问题与以下方面有关:

多态性 vs 覆盖 vs 重载
尝试尽可能轻松地描述多态性

编辑:

好的,要调用的方法在编译时决定。是否有避免使用运算符的解决方法?instanceof


答案 1

这篇文章秒声voo的答案,并提供了有关后期绑定的详细信息/替代方案。

一般JVM只使用单调度:运行时类型只考虑接收方对象;对于方法的参数,将考虑静态类型。使用方法表(类似于C++的虚拟表)可以轻松实现具有优化的高效实现。您可以在热点维基中找到详细信息。

如果您想为参数进行多次调度,请查看

  • 时髦。但据我所知,它有一个过时的,缓慢的多调度实现(例如,请参阅此性能比较),例如没有缓存。
  • clojure,但这与Java完全不同。
  • MultiJava,它为Java提供多种调度。此外,您可以使用
    • this.resend(...)而不是调用封闭方法的最具体的重写方法;super(...)
    • 值调度(下面的代码示例)。

如果你想坚持使用Java,你可以

  • 通过在更细粒度的类层次结构上移动重载方法来重新设计应用程序。Josh Bloch的《Effective Java》 Item 41(明智地使用重载)中给出了一个例子。
  • 使用一些设计模式,如策略,访客,观察者。这些通常可以解决与多次调度相同的问题(即,在这些情况下,您可以使用多个调度来为这些模式提供简单的解决方案)。

值调度:

class C {
  static final int INITIALIZED = 0;
  static final int RUNNING = 1;
  static final int STOPPED = 2;
  void m(int i) {
    // the default method
  }
  void m(int@@INITIALIZED i) {
    // handle the case when we're in the initialized `state'
  }
  void m(int@@RUNNING i) {
    // handle the case when we're in the running `state'
  }
  void m(int@@STOPPED i) {
    // handle the case when we're in the stopped `state'
  }
}

答案 2

你想要的是双倍或更通用的多重调度,实际上是用其他语言实现的(想到了常见的lisp)

据推测,Java没有它的主要原因是因为它会降低性能,因为重载解析必须在运行时而不是编译时完成。解决这个问题的通常方法是访客模式 - 非常丑陋,但事实就是这样。