Java 泛型方法。为什么T被推导为Map?

Java generic method. Why is T deduced to be Map?

考虑以下代码

class MyClass {
    public MyClass(Map<String, String> m) {
        System.out.println("map");
    }

    public MyClass(SortedMap<String, String> m) {
        System.out.println("sortedmap");
    }
}
public class Test {

    public <T extends Map<String,String>> Test(T t) {
        new MyClass(t);
    }

    public static void main(String[] args) {
        new Test(new TreeMap<String,String>());
    }

}

它打印 map。为什么 T 被推断为 Map 而不是 public <T extends Map<String, String>> Test(T t) 中的 SortedMap?有没有办法改变这种行为,以便为 MyClass 使用最具体的构造函数?

解析调用MyClass的构造函数是在编译时完成的。当编译器编译Test构造函数的代码时,它不知道T到底是什么,它只知道它保证是Map<String, String>,所以它不能做任何其他事情而不是将构造函数调用绑定到采用 Map 的构造函数。 在您的代码中 TTreeMap 的知识仅存在于方法 main 的主体内,而不存在于外部。例如,考虑如果您添加 Test 构造函数的第二个调用者实际传递 HashMap.

会发生什么

Java 泛型的工作原理是,泛型方法的代码只为所有可能的泛型参数值编译一次(并且只在字节码中出现一次),不像其他语言那样有一个副本每个泛型类型的泛型方法。

一般来说,在 Java 中不可能让代码中的单个 method/constructor 调用实际调用 不同的 methods/constructors运行时取决于参数的类型。这仅适用于取决于被调用对象的运行时类型的方法调用(使用覆盖方法的动态绑定)。

通过查看参数的静态类型,重载(您在此处拥有的)仅在编译时起作用。

这种情况的典型解决方案是在 MyClass 的构造函数中使用 instanceof SortedMap 检查。 另一个可能的(更优雅的)解决方案是访问者模式,但这只适用于为它准备的 classes(所以如果你不将它们包装在 [=39= 中,则不适用于 Map 个实例] 自己的)。