Java泛型:put()在Map <string,capture#3-of?=“”extends =“”abstractclass =“”>不适用于参数(String,AbstractClass)</string,capture#3-of>

时间:2012-12-09 14:27:06

标签: java generics capture

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

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

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

“方法put(String,capture#3-of?extends AbstractClass)在Map类型中不适用于参数(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);
    }

}

3 个答案:

答案 0 :(得分:1)

如果您只想存储任何AbstractClass,只需将地图声明为

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

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

答案 1 :(得分:1)

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

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

使用一个更简单的示例:如果一个方法返回Map<String, ? extends Number>,那么编译器就不会知道将一个Integer添加到Map是否安全,因为Map的实际非通配符类型可能是{{ 1}},Map<String, Double>

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

Map<String, Short>

答案 2 :(得分:0)

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

给定一个带有此签名的方法:

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

和任何引用类型的变量:

<any reference type> bar;

无法将barbar.getClass()传递给此方法:

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

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

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

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

1)使用与引用相同的类型来转换要参数化的类对象(即使这在技术上不是真的):

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

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);
}

(如果你不想进行未经检查的演员表,你可以AbstractClass.getNameMap(clazz).put(name, clazz.cast(obj));