【问题标题】:Java generic method. Why is T deduced to be Map?Java 泛型方法。为什么推导出T是Map?
【发布时间】:2015-03-16 00:40:58
【问题描述】:

考虑下面的代码

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 &lt;T extends Map&lt;String, String&gt;&gt; Test(T t) 中的 SortedMap ?有没有办法改变这种行为,以便为MyClass 使用最具体的构造函数?

【问题讨论】:

    标签: java generics methods


    【解决方案1】:

    MyClass 的构造函数被调用的解析是在编译时完成的。当编译器编译Test构造函数的代码时,它不知道T实际上是什么,它只知道它保证是一个Map&lt;String, String&gt;,所以除了将构造函数调用绑定到采用Map 的构造函数。 在您的代码中 TTreeMap 的知识仅存在于方法 main 的主体中,而不存在于外部。例如,如果您添加第二个调用者 Test 构造函数实际上传递了 HashMap,会发生什么情况。

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

    一般来说,Java 中不可能让代码中的单个方法/构造函数调用在运行时根据参数的类型实际调用不同 方法/构造函数。这仅适用于方法调用,具体取决于被调用对象的运行时类型(与覆盖方法的动态绑定)。

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

    这种情况的典型解决方案是在MyClass 的构造函数中使用instanceof SortedMap 检查。 另一种可能的(更优雅的)解决方案是访问者模式,但这仅适用于为其准备的类(因此不适用于 Map 实例,如果您不将它们包装在自己的类中)。

    【讨论】:

    • 编译器怎么不知道T实际上是什么类型?我将它构造为TreeMap(如果调用构造函数并传递一个声明为Map的对象确实是TreeMap,我会理解这种行为,但事实并非如此)
    • 哦,我期待在相同的通用方法中为不同版本的T 生成不同的代码。我猜Java不是C++。感谢您的回答,现在很清楚了。
    猜你喜欢
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-17
    • 2010-09-06
    • 2021-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多