问题
Is there a way one could avoid type erasure and get access to a type parameter?
public class Foo<T extends Enum<?> & Bar> {
public Foo() {
// access the template class here?
// i.e. :
baz(T.class); // obviously doesn't work
}
private void baz(Class<T> qux) {
// do stuff like
T[] constants = qux.getEnumConstants();
...
}
}
I need to know about T
, and do things with it. Is it possible, and if so, how can it be done without passing in the class in the constructor or anywhere besides the parameter?
EDIT: The main purpose of this question is to find out if there is any practical way around type erasure.
回答1:
AFACT, there is no practical way around type erasure because you can't ask for something which the runtime doesn't have access to. Assuming of course you agree that sub-classing generic classes for each enum which implements Bar
interface is a practical work around.
enum Test implements Bar {
ONE, TWO
}
class Foo<T> extends FooAbstract<Test> {
public Foo() {
ParameterizedType genericSuperclass =
(ParameterizedType) getClass().getGenericSuperclass();
baz((Class<T>) genericSuperclass.getActualTypeArguments()[0]);
}
private void baz(Class<T> qux) {
T[] constants = qux.getEnumConstants();
System.out.println(Arrays.toString(constants)); // print [ONE, TWO]
}
}
interface Bar {
}
class FooAbstract<T extends Enum<?> & Bar> {
}
回答2:
If you're willing/able to hand a class token to the constructor:
public Foo(Class<T> clazz) {
baz(clazz);
}
private void baz(Class<T> qux) {
// ...
}
That way, you can produce objects of type T with Class.newInstance(), attempt to cast arbitrary objects to T using Class.cast(), etc.
What do you intend to do in baz()?
回答3:
As pholser points out in his answer, the only way to achieve this is by passing in the Class
object representing the type T
. It's because of Type Erasure that something like T.class
isn't possible because T
is erased before runtime.
You seem resistant against passing in the Class
object, but it's the only way to use the method getEnumConstants(). Here is a self contained example:
public class Foo<T extends Enum<?> & Bar> {
final Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz = clazz;
}
public void baz() {
T[] constants = clazz.getEnumConstants();
System.out.println(Arrays.toString(constants));
}
public static void main(String[] args) {
new Foo<MyEnum>(MyEnum.class).baz(); //prints "[A, B, C, D]"
}
}
public interface Bar { }
public enum MyEnum implements Bar { A, B, C, D; }
回答4:
Use a super type token as proposed by Neil Gafter and used by libraries like guice for this purpose.
See http://gafter.blogspot.com/2006/12/super-type-tokens.html for the original description and I've check out the guice source for CA radio life working implementation.
there is another q which has an answer with worked example inline here How can I pass a Class as parameter and return a generic collection in Java?
回答5:
In some cases you can use a workaround suggested by Richard Gomes. When creating instances of anonymous classes, the type parameter class info is available.
class A<T>
{
A()
{
java.lang.reflect.ParameterizedType parameterizedType = (java.lang.reflect.ParameterizedType) (this.getClass().getGenericSuperclass());
System.out.println(parameterizedType.getActualTypeArguments()[0]);
}
}
public class Example {
public static void main(String[] args) {
A<String> anonymous = new A<String>() {}; // prints java.lang.String
}
}
Note that multiple instances created this way will be of different anonymous classes, and if that's a problem you might want a class A_String_Factory with a createNew() function based on clone to replace the calls to new.
来源:https://stackoverflow.com/questions/8452893/avoiding-java-type-erasure