Reputation: 2268
I'm trying to pull off some Guava TypeToken
magic for a class which is parameterized with a generic type parameter, but I am getting a ClassNotFoundException
when trying to instantiate it through the Class.forName(String)
method.
public class EnumeratedMenu<E extends Enum<E>,T extends EnumeratedBean<E>> {
private final TypeToken<EnumeratedMenu<E,T>> typeToken =
new TypeToken<EnumeratedMenu<E,T>>(getClass()) { };
private Class<T> getBean() {
TypeToken<?> genericBeanParam = typeToken.resolveType(EnumeratedMenu.class.getTypeParameters()[1]);
try {
//This generates a ClassNotFoundException because of the generic type parameter that EnumeratedBean has.
return (Class<T>)Class.forName(genericBeanParam.getType().getTypeName());
} catch(ClassNotFoundException e) {
log.error("Unable to find the class definition for {}", genericBeanParam.getType().getTypeName());
log.catching(e);
}
}
}
I am getting this exception:
"Unable to find the class definition for EnumeratedBean<ContextMenuOption$ContextType>
"
ContextMenuOption$ContextType
is the runtime type of the generic enum
parameter for EnumeratedBean
. How do I get the class object I want?
Clarification:
I need to construct and instance of the class to use with a builder object that will construct a menu option like this below:
T optionBean = beanFactory.create(beanClass);
IEnumeratedMenuOptionBuilder<E,T> builder = new EnumeratedMenuOptionBuilder<>(optionBean);
builder.setDriver(getDriver()).setTimeoutInSeconds(getTimeoutInSeconds())
.setEnumerationFunction(getEnumerationFunction());
Upvotes: 1
Views: 3150
Reputation: 110094
What are you actually trying to do here?
You're never going to be able to get a Class<T>
object that represents a generic type like EnumeratedBean<ContextMenuOption$ContextType>
, because Class
can only represent a raw type like EnumeratedBean
.
It's possible that there's some other way to achieve the result you want, but it's not clear what that result you want is currently. For example, what do you want to use the Class
you're returning for? Perhaps it's possible to do that without the intermediate Class
object.
Edit:
Since it looks like what you wanted was to be able to create an instance of T
, here's an example of how you could do it using TypeToken
:
public class EnumeratedMenu<E extends Enum<E>, T extends EnumeratedBean<E>> {
// Just get the type of T, that's what we care about here
private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {};
private T create() {
try {
// Using the raw type Class object for the token to
// get at its default constructor.
return typeToken.constructor(typeToken.getRawType().getConstructor())
.invoke(null);
} catch (Exception e) {
// ...
}
}
}
Upvotes: 4
Reputation: 116580
This is impossible to do, as there is always just one single Class
, regardless of parameterizations. This is how Java generics work with type erasure.
For detailed explanation of how and why, you may want to read:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html
But as to instantiation you will simply need to use the Class
you already have; and cast generic type signature the way you want.
This means, for example, that you can actually do:
List<String> strings = new ArrayList<String>();
List<Integer> numbers = (List<Integer>)(List<?>) strings;
numbers.add(Integer.valueOf(12));
without runtime exceptions. There is no real runtime information limiting contents; only implicit casting. You would, however, get a runtime exception if you tried:
String str = strings.get(0);
because while underlying List
happily accepts any java.lang.Object
s, access code has "hidden" cast, which will trigger exception.
So, instantiation may work something like:
List<String> s = (List<String>) ArrayList.class.newInstance();
but using whatever generic type you want.
EDIT:
And if Guava's TypeToken
is needed, its javadocs suggest you should either get raw class with getRawType()
, or perhaps directly create instance with constructor()
.
Upvotes: 2