Java 泛型: put() on Map<String,capture#3-of ?extends AbstractClass> 不适用于参数 (String, AbstractClass)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13788294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 13:59:08  来源:igfitidea点击:

Java generics: put() on Map<String,capture#3-of ? extends AbstractClass> is not applicable for the arguments (String, AbstractClass)

javagenericscapture

提问by user1889540

I'm trying to implement a sort of intern factory for multiple classes that extend from a common parent. Much of the logic is identical, but it can't really be inherited because the lookups need to be static. The desired syntax is something like:

我正在尝试为从公共父级扩展的多个类实现一种实习工厂。大部分逻辑是相同的,但不能真正继承,因为查找需要是静态的。所需的语法类似于:

Car c = AbstractClass.valueOf(Car.class, "Ford");

with Car having specific methods related to cars, but the instances are stored in a common cache. Here's what I have so far. My compile error is on the put in the constructor:

Car 具有与汽车相关的特定方法,但实例存储在公共缓存中。这是我到目前为止所拥有的。我的编译错误出现在构造函数中:

"The method put(String, capture#3-of ? extends AbstractClass) in the type Map is not applicable for the arguments (String, AbstractClass)"

“类型 Map 中的 put(String, capture#3-of ? extends AbstractClass) 方法不适用于参数 (String, AbstractClass)”

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public abstract class AbstractClass {

    private static Map<Class<? extends AbstractClass>, LinkedHashMap<String, ? extends AbstractClass>> map = new HashMap<Class<? extends AbstractClass>, LinkedHashMap<String, ? extends AbstractClass>>();

    private static synchronized <T extends AbstractClass> Map<String, T> getNameMap(Class<T> clazz) {
        LinkedHashMap<String, T> nameToEnum = (LinkedHashMap<String, T>) map.get(clazz);
        if (nameToEnum == null) {
            nameToEnum = new LinkedHashMap<String, T>();
            map.put(clazz, nameToEnum);
        }

        return nameToEnum;
    }

    public static <T extends AbstractClass> T valueOf(Class<T> clazz, String name) {
        return getNameMap(clazz).get(name);
    }

    public static <T extends AbstractClass> Collection<T> VALUES(Class<T> clazz) {
        return getNameMap(clazz).values();
    }

    public static <T extends AbstractClass> Set<T> SORTED_VALUES(Class<T> clazz) {
        return new TreeSet<T>(getNameMap(clazz).values());
    }

    AbstractClass(String name) {
        AbstractClass.getNameMap(this.getClass()).put(name, this);
    }

}

采纳答案by VGR

According to the javadoc for Object.getClass(), the returned type is a wildcard based compile-time type of the expression. Since the compiler only knows that thisreturns an AbstractClass instance, this.getClass()returns Class<? extends AbstractClass>.

根据 javadoc for Object.getClass(),返回的类型是基于通配符的表达式编译时类型。由于编译器只知道this返回一个 AbstractClass 实例,因此this.getClass()返回Class<? extends AbstractClass>.

This means your call to getNameMapin the constructor will return a Map<String, ? extends AbstractClass>. Which means that, while the returned Map has values of a specific (non-wildcard) type, that exact type isn't known at compile-time; the compiler only knows the Map's values are required to be either AbstractClass or something that inherits from AbstractClass. So the compiler can't safely add thisas a value, since it isn't known at compile-time which subtype of AbstractClass thisrepresents.

这意味着您getNameMap在构造函数中的调用将返回一个Map<String, ? extends AbstractClass>. 这意味着,虽然返回的 Map 具有特定(非通配符)类型的值,但该确切类型在编译时是未知的;编译器只知道 Map 的值必须是 AbstractClass 或从 AbstractClass 继承的东西。所以编译器不能安全地添加this一个值,因为在编译时不知道 AbstractClass 的哪个子类型this代表。

To use a simpler example: if a method returned Map<String, ? extends Number>then the compiler wouldn't know whether it was safe to add an Integer to the Map, because the Map's actual, non-wildcard type might be Map<String, Double>, Map<String, Short>, etc.

要使用一个简单的例子:如果返回的方法,Map<String, ? extends Number>那么编译器将不知道是否是安全的添加的整数地图,因为地图的实际,非通配符类型可能是Map<String, Double>Map<String, Short>

As for a solution: I don't think there is a way to have a Map use generics to match each individual key's type with its corresponding value's type. I would forget about using bounded types on the inner Maps' values, and use dynamic casting instead:

至于解决方案:我认为没有办法让 Map 使用泛型将每个单独的键的类型与其对应的值的类型相匹配。我会忘记在内部 Maps 的值上使用有界类型,而是使用动态转换:

private static Map<Class<? extends AbstractClass>, Map<String, AbstractClass>> map = new HashMap<>();

private static synchronized Map<Class<? extends AbstractClass>, Map<String, AbstractClass>> getNameMap(Class<T> clazz) {
    // same as before
}

public static <T extends AbstractClass> T valueOf(Class<T> clazz, String name) {
    return clazz.cast(getNameMap(clazz).get(name));
}

回答by Vikdor

If you just want to store anything that is an AbstractClass, just declare your map as

如果您只想存储任何属于 AbstractClass 的内容,只需将您的地图声明为

private static Map<Class<? extends AbstractClass>, LinkedHashMap<String, AbstractClass>> map = 
    new HashMap<Class<? extends AbstractClass>, LinkedHashMap<String, AbstractClass>>();

This would allow you to store any instance of AbstractClass or its subclasses in the inner map, against AbstractClass or one of its sub class.

这将允许您在内部映射中针对 AbstractClass 或其子类之一存储 AbstractClass 或其子类的任何实例。

回答by newacct

Your problem can basically be boiled down to this:

你的问题基本上可以归结为:

Given a method with this signature:

给定具有此签名的方法:

public static <T> void foo(T x, Class<T> y);

and a variable of any reference type:

和任何引用类型的变量:

<any reference type> bar;

it is impossible to pass barand bar.getClass()to this method:

不可能通过barbar.getClass()到这个方法:

foo(bar, bar.getClass()); // error

even though it is provable that there always exists some Tfor which it is correct (i.e. T= the actual runtime type of bar).

即使可以证明总是存在一些T它是正确的(即T= 的实际运行时类型bar)。

It is due to the special case in the language for the type of .getClass()that causes this problem.

这是由于.getClass()导致此问题的类型的语言中的特殊情况。

I can think of two ways to solve this:

我可以想到两种方法来解决这个问题:

1) Cast the class object to be parameterized by the same type as the reference (even though this is technically not true):

1) 将要参数化的类对象强制转换为与引用相同的类型(即使这在技术上是不正确的):

AbstractClass(String name) {
    AbstractClass.getNameMap((Class<AbstractClass>)this.getClass()).put(name, this);
}

2) Cast the object to the same type as the parameter of the class method. This will require a capture helper due to the wildcard in the class's type:

2) 将对象强制转换为与类方法的参数相同的类型。由于类类型中的通配符,这将需要一个捕获助手:

private static <T> void helper(Class<T> clazz, String name, Object obj) {
    AbstractClass.getNameMap(clazz).put(name, (T)obj);
}
AbstractClass(String name) {
    helper(this.getClass(), name, this);
}

(if you don't want that unchecked cast you can do AbstractClass.getNameMap(clazz).put(name, clazz.cast(obj));)

(如果您不想要未经检查的演员阵容,您可以这样做AbstractClass.getNameMap(clazz).put(name, clazz.cast(obj));