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:
“The method put(String, capture#3-of ? extends AbstractClass) in the type Map is not applicable for the arguments (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);
}
}
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 thatthisreturns an AbstractClass instance,this.getClass()returnsClass<? extends AbstractClass>.This means your call to
getNameMapin the constructor will return aMap<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 addthisas a value, since it isn’t known at compile-time which subtype of AbstractClassthisrepresents.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 beMap<String, Double>,Map<String, Short>, etc.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: