I am having a hard time declaring a Map using Generics. I'd like to declare a Map with the following properties:
The key is a Class object of any Type T derived from a particular Interface (IFoo)
The value of the Map is another Map whose key is an String and whose value is of the Type T
I thought I can do it like this:
public static Map<Class<T extends IFoo>, Map<String, T>> valueCache =
new HashMap<Class<T extends IFoo>, Map<String, T>>();
I get a syntax error on "extends"
If I replace the T Types with wildcards (?) like this it seems to be syntactically correct:
public static Map<Class<? extends Typ>, Map<Integer, ?>> valueCache=
new HashMap<Class<? extends Typ>, Map<Integer, ?>>();
But I don't think this is what I want since I want to be exactly the type of the Class object to be the value in the second map.
Any help appreciated.
A generic type parameter can only be declared on a class or method declaration.
If you don't care about the reference type of the IFoo that you get back you can do
static Map<Class<? extends IFoo>, Map<String, IFoo>> fooMap;
If you want to use the IFoo returned as its subclass type then you need to do some casting.
// abbreviated example
class FooMap {
private static Map<Class<? extends IFoo>, Map<String, IFoo>> map = ...;
static void put(String key, IFoo foo) {
map.get(foo.getClass()).put(key, foo);
}
static <F extends IFoo> F get(Class<F> cls, String key) {
return cls.cast(map.get(cls).get(key));
}
}
FooMap.put("foo", new Foo());
Foo foo = FooMap.get(Foo.class, "foo");
Move the extends into your class's generic definition:
public class ClassWithGeneric<T extends IFoo> {
Map<Class<T>, Map<String, T>> valueCache = new HashMap<Class<T>, Map<String, T>>();
}
It's not technically possible to do what you want, but you can simulate it using accessor methods with internal casting. For example:
private static Map<Class<?>, Map<String, ?>> valueCache = new HashMap<>();
public <T extends IFoo> Map<String, T> getMap(Class<T> key) {
return (Map<String, T>)value cache.get(key);
}
Try this,
public static Map<IFoo, Map<String, IFoo>> valueCache = new HashMap<IFoo, Map<String, IFoo>>();
By this way, you make use of the map for IFoo Type Classes.
since you Declaring a Map you should Specify all types it helps a lot and should be always Correct so it should be like T should be a type and not Generic since you declare it so java wants to know the type in the newer Versions of java you dont need to declare the second <> generics so i dont know the second type of your inner map so i used Object
public static Map<IFoo,Map<String,Object>valueChache=new Hashmap<>();
Both should be Correct
public static Map<IFoo,Map<String,Object>valueChache=new Hashmap<IFoo,Map<String,Object>();
Related
I have a class Saver<T> with one generic type argument <T>. In my Saver<T> class, I'm attempting to define the following static map:
public abstract class Saver<T> {
private static final Map<Class<E>, Class<? extends Saver<E>>> DEFAULT_SAVERS = new HashMap<>();
}
Of course, this gives me an error because the compiler doesn't know what E is. How can I create this map? The map should have Classes for keys, and Savers with that class as its generic type as its values. Is this possible? How can I do this?
There's no typesafe way to declare it. You'll have to use wildcards and control how it's accessed to prevent heap pollution:
private static Map<Class<?>, Saver<?>> DEFAULT_SAVERS = new HashMap<>();
public static <T> void put(Class<T> clazz, Saver<T> saver) {
DEFAULT_SAVERS.put(clazz, saver);
}
public static <T> Saver<T> get(Class<T> clazz) {
return (Saver<T>)DEFAULT_SAVERS.get(clazz);
}
I have been trying to return a Map using generics:
import java.util.HashMap;
import java.util.Map;
public class GenericTest {
public <T extends Vehicle> Map<String, T> get() {
Map<String, Car> map = new HashMap<>();
return map; // Getting error
}
}
class Vehicle {
}
class Car extends Vehicle {
}
In the line, where I am returning map , i am getting error
Type mismatch: cannot convert from Map<String,Car> to Map<String,T>
Could somebody explain why??
In the return type I have mentioned that T extends Vehicle.
Because you can call Map<String, Truck> map = new GenericTest().get();. And a Map<String, Car> isn't a Map<String, Truck>.
Change the variable declaration to:
Map<String, T> map = new HashMap<>();
or change the method signature to:
public Map<String, Car> get()
This is a common misconception that generic parameters are determined by the callee (the method), not the caller.
In fact, generic parameters are determined by the caller i.e. when you call the method:
Map<String, Bus> myMap = get(); // Now "T" is "Bus".
That's why the error occurs. As the caller, I can specify any Vehicle as T, but the method can only return a Map<String, Car>, which is incompatible with Map<String, Bus>.
To do what you want to do, change the return type to Map<String, ? extends Vehicle>.
you are defining return type as generics but returning concrete object's map. According to your method signature public <T extends Vehicle> Map<String, T> get() you could assign returned value to map of any subclass of Vehicle as value and key as String but since you are returning Map<String, Car> you could not do that.
so you need to change map to Map<String, T> map = new HashMap<>()
public class GenericTest {
public <T extends Vehicle> Map<String, T> get() {
Map<String, T> map = new HashMap<>();
return map;
}
}
class Vehicle {
}
class Car extends Vehicle {
}
Then you call the method like this
Map map = new GenericTest().get();
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 that this returns an AbstractClass instance, this.getClass() returns Class<? extends AbstractClass>.
This means your call to getNameMap in 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 this as a value, since it isn't known at compile-time which subtype of AbstractClass this represents.
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.
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:
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));
}
If you just want to store anything that is an AbstractClass, just declare your map as
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.
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 bar and bar.getClass() to this method:
foo(bar, bar.getClass()); // error
even though it is provable that there always exists some T for which it is correct (i.e. T = the actual runtime type of bar).
It is due to the special case in the language for the type of .getClass() that causes this problem.
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):
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:
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));)
I have a Map whose keys are of generic type Key<T>, and values are of type List<T>. If the key is an instance of Key<String>, the value must be a List<String>, and the same rule applies to any other key-value pairs. I have tried the following but it does not compile:
Map<T, List<T>> map;
At present I have to declare it with "partial" generics:
Map<Object, List> map;
I know this is bad but I currently have no better choice. Is it possible to use generics in this situation?
UPDATE
Maybe I didn't express my problem clearly. I want a map that is able to:
map.put(new Key<String>(), new ArrayList<String>());
map.put(new Key<Integer>(), new ArrayList<Integer>());
And the following code should not compile:
map.put(new Key<String>(), new ArrayList<Integer>());
The key and value should always have the same generic type while the generic type can be any, and obviously extending a map does not meet my requirement.
I'm not aware of any existing library that does precisely this but it is not too hard to implement yourself. I've done something similar a few times in the past. You cannot use the standard Map interface but you can use a hash map inside to implement your class. To start, it might look something like this:
public class KeyMap {
public static class Key<T> { }
private final HashMap<Object,List<?>> values = new HashMap<Object,List<?>>();
public <T> void put(Key<T> k, List<T> v) {
values.put(k, v);
}
public <T> List<T> get(Key<T> k) {
return (List<T>)values.get(k);
}
public static void main(String[] args) {
KeyMap a = new KeyMap();
a.put(new Key<String>(), new ArrayList<String>());
a.get(new Key<Integer>());
}
}
This is what you want:
public class Test<T> extends HashMap<T, List<T>>
{
}
If you don't want a HashMap as the super class then change it to whatever concrete class you want.
I want to create a mapping from (a) class type to (b) long (the identifier of the object of the defined class type) to (c) the object itself.
I have the following:
protected HashMap<Class<?>, HashMap<Long, ?>> obj = new HashMap<Class<?>, HashMap<Long, ?>>();
Is it possible to somehow denote that the first ? must be of the same type than the second ?? I would expect something like this, but this is ofcourse not possible:
protected <T> HashMap<Class<T>, HashMap<Long, T>> obj = new HashMap<Class<T>, HashMap<Long, T>>();
As an alternative, you could use a small amount of not-type-safe code encapsulated in a way that enforces your constraint:
class Cache {
private Map<Class<?>, Map<Long, ?>> items = new HashMap<Class<?>, Map<Long, ?>>();
private <T> Map<Long, T> getItems(Class<T> type) {
#SuppressWarnings("unchecked")
Map<Long, T> result = (Map<Long, T>) items.get(type);
if (result == null) {
result = new HashMap<Long, T>();
items.put(type, result);
}
return (Map<Long, T>) result;
}
public <T> void addItem(Class<T> type, Long id, T item) {
getItems(type).put(id, item);
}
public <T> T getItem(Class<T> type, Long id) {
return type.cast(getItems(type).get(id));
}
}
The type.cast() in getItem() isn't necessary for the compiler to not complain, but it would help catch an object of the wrong type getting into the cache early.
Each occurence of a wildcard corresponds to a different type, and the only appropriate scope for a type parameter representing the type is the entry in the outer HashMap. Unfortunately, HashMap does not allow constraining the entry type in its type parameter like:
class Entry<K,V> {
// fields omitted
}
class Map<E extends Entry<?,?> {
}
class EntityCacheEntry<E> extends Entry<Class<E>, Map<Entry<Long, E>>> { }
class EntityCache extends Map<EntityCacheEntry<?>> { }
Even if it did, there is no way to implement Map.get without using unchecked casts, because we'd have to constrain its type parameter to a particular member of the type family represented by E - and you can't constrain a type parameter of a type parameter in Java.
Therefore, your only recourse is writing a facade whose api enforces the type invariant, but internally uses casts:
class EntityCache {
Map<Class<?>, Map<Long, Object>> map = new HashMap<>();
public <E> void put(Class<E> clazz, long id, E entity) {
map.get(clazz).put(id, entity);
// TODO create map if it doesn't exist yet
}
public <E> E get(Class<E> clazz, long id) {
return clazz.cast(map.get(clazz).get(id));
// TODO what if not found?
}
}
You could extend the HashMap class with your specific generic definitions and make a generic class that takes the <T> as argument, something like this:
public class MyHashMap<T> extends HashMap<Class<T>, HashMap<Long, T>> { ...
What you probably want is: HashMap<Class<?>, HashMap<Long, Object>>. Because you will be putting objects of different types in it, Object is the type parameter that will allow you to add any type of object.
Don't get confused with the wildcard (?). It has the opposite meaning -- it means that the parameter is some type (thus all the objects must be of that type) but we don't know what it is, thus we can't put anything in it at all. That is not what you want.