I have a class which is generified with the type <T extends Enum<T> & Runnable>
. I have a member variable Class<T> bar
which I set by the class name: t = (Class<T>) Class.forName(name)
. This gives me an unchecked cast warning.
Normally, using asSubclass can be used in similar situations, but since T
has multiple bounds, I'm not able to use it without getting a compiler warning:
//This is type safe, but I still get an unchecked cast warning
t = (Class<T>) Class.forName(className).asSubclass(Enum.class).asSubclass(Runnable.class);
Can I get rid of this warning in any way, without using @SupressedWarning("unchecked")
?
Full example:
public class Example<T extends Enum<T> & Runnable> {
Class<T> t;
Example(String className) throws ClassNotFoundException {
t = (Class<T>) Class.forName(className).asSubclass(Enum.class).asSubclass(Runnable.class);
}
}
//This is type safe, but I still get an unchecked cast warning
t = (Class<T>) Class.forName(className).asSubclass(Enum.class).asSubclass(Runnable.class);
Whoa, slow down a second! Is that actually true? No it's not! All you've done is establish that the class matching the passed-in name is both a Runnable
and an Enum
, not that it is actually T
. You've only validated the bounds. Imagine we had classes T1
and T2
:
package foo;
public enum T1 implements Runnable {
;
@Override
public void run() {
}
}
package foo;
public enum T2 implements Runnable {
;
@Override
public void run() {
}
}
Then this works fine, but is obviously not type safe:
Example<T1> example = new Example<T1>("foo.T2");
Class<T1> t1Clazz = example.t; //uh oh...
This isn't a problem of multiple bounds either. You'd have the same problem with only one bound.
As @sp00m mentions, the real solution is probably to pass in Class<T>
here.
If, on the other hand, T
was only needed internally (i.e. to specify the multiple bounds) and need not actually be exposed, then another option is to maintain the class in two separate references. For example:
Class<? extends Runnable> runnableClass;
Class<? extends Enum> enumClass;
Example(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> clazz = Class.forName(className);
runnableClass = clazz.asSubclass(Runnable.class);
enumClass = clazz.asSubclass(Enum.class);
}
That's because, without the type parameter, there are very few circumstances where you could actually take advantage of the knowledge that it's an enum
and a Runnable
at the same time. If you create an instance of the class, you need to assign it to a variable/field of type Runnable
or Enum
; you can't do both.
I believe you simply can't... The best solution in my opinion would be to pass the Class<T> clazz
directly as parameter rather than its String name
.
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