I have a class which takes a type token, and then generates objects of a type parameterized by that type (OK, it's a bit more complicated than that, but this is a concise example):
public class Test {
public static void main(String[] args) throws Exception {
Holder<HashSet<Integer>> i = newObjectHolder(HashSet.class); // this fails
}
static class Holder<T> {
public Holder(T newInstance) {}
}
public static <T> Holder<T> newObjectHolder(Class<T> typeToken) throws Exception {
Constructor<T> ctor = typeToken.getConstructor();
return new Holder<T>(ctor.newInstance());
}
}
This works fine if passed non-generic types, like:
Holder<Integer> i = Test.newObjectHolder(Integer.class);
If the passed type token is generic, however, it doesn't work, as in the indicated line above:
Holder<HashSet<Integer>> i = Test.newObjectHolder(HashSet.class); // this fails
I get the problem, but is there a solution? I'm OK to add @SuppressWarnings("unused") in the code for newObject
, if it doesn't reduce the safety. Intuitively, it seems that newObject should be able to to make a cast that works, we know that one "new" object of an erased generic type is the same as any other, and we haven't otherwise used T
in the method.
So, sheepishly, answering my own question...
I can't find any way to do it with Class<?>
, but this magic technique called super type tokens seems to work well. Here's the updated code:
public class Test {
static class A {}
static class B extends A {}
public static void main(String[] args) throws Exception {
Holder<HashSet<Integer>> i = newObjectHolder(new TypeReference<HashSet<Integer>>() {}); // works
Holder<A> j = newObjectHolder(new TypeReference<A>() {}); // works
Holder<A> k = newObjectHolder(new TypeReference<B>() {}); // works
Holder<B> l = newObjectHolder(new TypeReference<A>() {}); // doesn't compile (good)
}
static class Holder<T> {
public Holder(T newInstance) {}
T get() { return null; }
}
public static <T,U extends TypeReference<? extends T>> Holder<T> newObjectHolder(U typeToken) throws Exception {
T obj = typeToken.newInstance();
return new Holder<T>(obj);
}
}
The TypeReference
code is given here, although I suspect that Guice's TypeLiteral
would work just as well (but it doesn't have newInstance code, you'd have to implement it).
In your, now updated, code, you still can
Holder<HashSet> i = newObjectHolder(HashSet.class);
but can't
Holder<HashSet<Integer>> i = newObjectHolder(HashSet.class);
Old answer:
I am not sure what problem you're getting. This code works alright.
public class Test {
public static void main(String[] args) throws Exception {
@SuppressWarnings("unchecked")
HashSet<Integer> i = newObject(HashSet.class);
i.add(new Integer(42));
i.add(new Integer(666));
System.out.println(i.size() +":" + Arrays.toString(i.toArray()));
}
public static <T> T newObject(Class<T> typeToken) throws Exception {
Constructor<T> ctor = typeToken.getConstructor();
return ctor.newInstance();
}
}
prints
2:[666, 42]
Am I missing anything?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With