I just implement a MapBuilder to build map easy,
But when i try to get an instance of HashMap.class,I suddenly found that I can't use HashMap.class to get such an instance.
It's illegal!
So can anybody tell me why and how to solve this problem?
The MapBuilder is follow:
import java.util.Map;
public abstract class MapBuilder {
public static <K, V, T extends Map<K, V>> InnerMapBuilder<T, K, V> start(
Class<T> clazz) {
return new InnerMapBuilder<>(clazz);
}
public static class InnerMapBuilder<T extends Map<K, V>, K, V> {
private T target;
public InnerMapBuilder(Class<T> clazz) {
try {
target = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public InnerMapBuilder<T, K, V> put(K key, V val) {
target.put(key, val);
return this;
}
public T get() {
return target;
}
}
}
And the test code is below:
public static void main(String[] args) {
HashMap<String, String> v = start(HashMap<String,String>.class).put("a", "b").get();
System.out.println(v);
}
It's impossible to obtain a parameterized class type variable for a generic type, as Reimeus has said. So you have three choices.
First, you can live with the unchecked cast:
Class<? extends Map<String, Integer>> clazz =
(Class<? extends Map<String, Integer>>) HashMap.class;
Second, you can reify the parameters for a class by extending it (in this example, using an anonymous inner class):
Class<? extends Map<String, Integer>> clazz =
new HashMap<String, Integer>() {}.getClass();
Or third, and best, just take the Map instance instead of a class in start(). You're not saving the user any work by taking the Class rather than an instance of Map, and the first thing you do is create an instance of it.
By passing it in, the user can even tweak the settings of the map (e.g. for a HashMap, set the load factor, for TreeMap, specify the Comparator) so it's a better alternative anyway. If you need to, you can assert that it's empty when it's passed in.
If for some reason you really need a factory, don't use Class: it doesn't work well as a factory, because the only way you can customize the instance that Class creates is by subclassing the class and providing a new no-arg constructor. Just create an interface Factory<T> that has a method T create() and then accept a Factory<? extends Map<K, V>.
First since start takes a class you would have to pass it an unparameterized class such as HashMap.class. Second, as you're returning a generic type Map, you would have to make your local type match also, so to use:
Map<String, String> v = start(HashMap.class).put("a", "b").get();
Related
This is a class, which I need to have an object from it to call its method.
public abstract class FileInputFormat<K, V> extends InputFormat<K, V> {
// ...
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
// ...
}
I tried this:
FileInputFormat<K, V> test = new FileInputFormat<K, V>();
but it shows errors.
What should I do?
You want to instantiate an object of a generic class, so you have to provide some concrete types in place of K and V, for example:
FileInputFormat<Integer, String> test=new FileInputFormat<Integer, String>();
In the case of generics,you need to replace K,V with some class names.
In your case,it may be
FileInputFormat<Integer, String> test=new FileInputFormat<Integer, String>();
OR
FileInputFormat<String,Integer> test=new FileInputFormat<String,Integer>();
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));)
The problem: I've a Function Object interface defined in a class:
public static interface FunctionObject<T> {
void process(T object);
}
I need it generic because I'd like to use T methods in the process implementations.
Then, in other generic class, I've a Map where I have classes as keys and function objects as values:
Map<Class<T>, FunctionObject<T>> map;
But I also want the map to accept subtype classes and function objects of supertypes OF THE KEY TYPE, so I did this:
Map<Class<? extends T>, FunctionObject<? super T>> map; //not what I need
The basic idea is to be able to use the map as follows:
//if T were Number, this should be legal
map.put(Class<Integer>, new FunctionObject<Integer>(){...});
map.put(Class<Float>, new FunctionObject<Number>(){...});
map.put(Class<Double>, new FunctionObject<Object>(){...});
As I want to enforce the FunctionObject has the type of the class key or a supertype, what I really would like to define is this:
Map<Class<E extends T>, FunctionObject<? super E>>> map;
How can I achieve the desired effect? Is a typesafe heterogenous container the only option? What would the Map generic types look like to allow populating it from a reference?
Parametrized container, seems to work just fine:
public class MyMap<T>
{
interface FunctionObject<X> {}
private Map<Class<? extends T>, FunctionObject<Object>> map = new HashMap<>();
#SuppressWarnings("unchecked")
public <E extends T> void put(Class<E> c, FunctionObject<? super E> f)
{
map.put(c, (FunctionObject<Object>) f);
}
public <E extends T> FunctionObject<Object> get(Class<E> c)
{
return map.get(c);
}
public static void Main(String[] args)
{
MyMap<Number> map = new MyMap<>();
map.put(Integer.class, new FunctionObject<Integer>() {});
map.put(Float.class, new FunctionObject<Number>() {});
map.put(Double.class, new FunctionObject<Object>() {});
}
}
Edited to comply to the question. Sadly there is no way to avoid the downcasting to object.
Edit added get().
You can do this with encapsulation, assuming you only use the map through the method which check this on a per entry basis.
The following add method avoids the need to double up on the type as well.
public class Main {
interface FunctionObject<T> { }
private final Map<Class, FunctionObject> map = new LinkedHashMap<Class, FunctionObject>();
public <T> void add(FunctionObject<T> functionObject) {
Class<T> tClass = null;
for (Type iType : functionObject.getClass().getGenericInterfaces()) {
ParameterizedType pt = (ParameterizedType) iType;
if (!pt.getRawType().equals(FunctionObject.class)) continue;
Type t = pt.getActualTypeArguments()[0];
tClass = (Class<T>) t;
break;
}
map.put(tClass, functionObject);
}
public <T> void put(Class<T> tClass, FunctionObject<T> functionObject) {
map.put(tClass, functionObject);
}
public <T> FunctionObject<T> get(Class<T> tClass) {
return map.get(tClass);
}
public static void main(String... args) throws IOException {
Main m = new Main();
m.add(new FunctionObject<Integer>() {
});
FunctionObject<Integer> foi = m.get(Integer.class);
System.out.println(foi.getClass().getGenericInterfaces()[0]);
}
}
prints
Main.Main$FunctionObject<java.lang.Integer>
You can use #SuppressWarnings("unchecked") if you want to disable the warning.
The point is; there is no way to describe the constraint you have in the field declaration, you can achieve the same result if you use accessor methods which do the check on a per entry basis. You can add runtime checks as well if you need to ensure raw types are correct.
I don't think the title of the question will be clear, but the idea is simple.
Suppose I have a Map type variable.
Map<K,V> myMap;
but I want to establish a relation between K and V. for example, I'd like to say that
this Map relates Sets of some class to objets of that class. Something like:
Map<Set<T>, T> myMap;
but not for a specific type T. I'd like this Map to accept entries like
(Set<String>, String),
(Set<Integer>, Integer)
...
Is there a possible declaration for myMap that allows me to have this behavior? Please let me know if I'm explaining myself wrongly or if I have a previous conceptual error.
Sadly this is not possible with Java generics. If Java allowed higher order type parameters, then one could have defined Map something like:
public interface Map<V<>> { // here V<> is my hypothetical syntax for a
// type parameter which is itself generic...
<K>
V<K> put(K key, V<K> value);
...
}
instead of the actual java.util.Map:
public interface Map<K, V> {
V put(K key, V value);
...
}
You can see that the problem is that K is declared once for the whole class and not for each call to .put().
Enough fantasizing, so what can you do? I think the best is to create a Map<Set<?>, Object> and wrap it as a private member. Then you are free to create your own put() and get() which take into account the intended "relation" between types:
class SpecialMap {
private Map<Set<?>, Object> map = ...;
public <T>
T put(Set<T> key, T value) {
return (T) map.put(key, value);
}
public <T>
T get(Set<T> key) {
return (T) map.get(key);
}
}
What you are trying to do doesn't seem like a good diea, because each Set<T> is always not equal to another Set<T> even if of the same type - using Sets as keys is more or less useless.
That said, you don't need to define a new class - you can require a method to accept such a map:
public static <T> void process(Map<Set<T>, T> map) {
for (Map.Entry<Set<T>, T> entry : map) {
Set<T> key = entry.getKey();
T value = entry.getValue();
// do something
}
}
I don't think it's possible to achieve compile-time checking with Java generics. However it's quite simple at runtime. Just right a short decorator:
public class FancyTypeMapDecorator implements Map<Set<? extends Object>, Object> {
final Map<Set<? extends Object>, Object> target;
public FancyTypeMapDecorator(Map<Set<? extends Object>, Object> target) {
this.target = target;
}
#Override
public Object put(Set<? extends Object> key, Object value) {
final Class<?> keyElementType = key.iterator().next().getClass();
final Class<?> valueType = value.getClass();
if (keyElementType != valueType) {
throw new IllegalArgumentException(
"Key element type " + keyElementType + " does not match " + valueType);
}
return target.put(key, value);
}
#Override
public void putAll(Map<? extends Set<? extends Object>, ? extends Object> m) {
for (Entry<? extends Set<? extends Object>, ? extends Object> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
//remaining methods are simply delegating to target
}
Here's how it works:
final Map<Set<? extends Object>, Object> map =
new FancyTypeMapDecorator(new HashMap<Set<? extends Object>, Object>());
Set<? extends Object> keyA = Collections.singleton(7);
map.put(keyA, 42);
Set<? extends Object> keyB = Collections.singleton("bogus");
map.put(keyB, 43);
Second put throws an exception.
However both the implementation (and I don't even mean that it will fail for empty Set as a key) and usage/API triggers an alarm bell... Do you really want to deal with such a structure? Maybe you need to rethink your problem? What are you actually trying to achieve?
There is no way with generics to have the compiler verify a different T for each put() call. In other words, you can't have the same map and do:
myMap.put(new HashSet<String>(), "foo");
myMap.put(new HashSet<Integer>(), 1);
If you need this then you may have to store <Object> and do the verification yourself using instanceof or some other hack.
Now, you can definitely do something like:
public class MyMap<T> extends HashMap<Set<T>, T> {
...
Then you can do:
MyMap<String> myMap = new MyMap<String>();
Set<String> set = new HashSet<String>();
myMap.put(set, "foo");
Remember that the key has to have a valid hashCode() and equals() methods which might be expensive with a Set.
Is it possible to tie nested generics/captures together?
I often have the problem of having a Map lookup of class to genericized item of said class. In concrete terms I want something like this (no, T is not declared anywhere).
private Map<Class<T>, ServiceLoader<T>> loaders = Maps.newHashMap();
In short, I want loaders.put/get to have semantics something like these:
<T> ServiceLoader<T> get(Class<T> klass) {...}
<T> void put(Class<T> klass, ServiceLoader<T> loader) {...}
Is the following the best I can do? Do I have to live with the inevitable #SuppressWarnings("unchecked") somewhere down the line?
private Map<Class<?>, ServiceLoader<?>> loaders = Maps.newHashMap();
Let me see If I got your intention: you want a map that stores pairs of Class/ServiceLoader where each pair is parameterized by the same T, but T may be different across pairs?
If this is the case then the best solution is to declare your own class which will exhibit such an interface. Internally it will store these pairs in a generic Map<Class<?>,ServiceLoader<?>> map.
public class MyMap {
private Map<Class<?>, ServiceLoader<?>> loaders
= new HashMaps<Class<?>, ServiceLoader<?>>();
public<T> void put(Class<T> key, ServiceLoader<T> value) {
loaders.put(key, value);
}
#SuppressWarnings("unchecked")
public<T> T get(Class<T> key) { return (ServiceLoader<T>) loaders.get(key); }
}
#SuppressWarnings("unchecked") annotations are not pure evil. You should try to avoid them but there are certain cases where you can figure out that the cast is correct despite the fact that the compiler cannot see that.
My suggestion is to create a new Object for such case. I see you were using Maps.newHashMap() so I take it that you used Google Guava so I will use ForwardingMap.
public class Loader<T> extends ForwardingMap<Class<T>, ServiceLoader<T>> {
private Map<Class<T>, ServiceLoader<T>> delegate = Maps.newHashMap();
}
A simple test proved that my suggestion is working:
public class Loader<T> extends ForwardingMap<Class<T>, Class<T>> {
private Map<Class<T>, Class<T>> delegate = Maps.newHashMap();
#Override protected Map<Class<T>, Class<T>> delegate() {
return delegate;
}
public static void main(String[] args) {
Loader<Integer> l = new Loader<Integer>();
l.put(Integer.class, Integer.class);
// error
l.put(Integer.class, String.class);
}
}