我正在尝试为来自普通父级的多个类实现一种实习工厂。大部分逻辑是相同的,但它不能真正继承,因为查找需要是静态的。所需的语法类似于:
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);
}
}
答案 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;
无法将bar
和bar.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));
)