I need to instantiate generic variable like Class>.
For example,
Class<? extends List<String>> = ...
Variant 1:
Class<? extends List<String>> clazz = LinkedList.class;
don't work - "Incompatible types".
Variant 2:
Class<? extends List> clazz = LinkedList.class;
work, but in this case List is a raw type - not good.
How to instantiate such variables?
There is not really a point in having Class<List<String>>, since it would be equivalent to Class<List<Integer>> (both being a reference to the raw List due to erasure).
Having said that, if your intention is to represent the List<String> type, this is possible, see TypeLiteral of Google Guice.
TypeLiteral<List<String>> type = new TypeLiteral<List<String>>() {};
System.out.println(type.getType()); //should print List<String>
The mechanism that makes it possible is reflection on static type declarations, in either class definition (class X extends Foo<Y>, Y is available through reflection, it is preserved in the bytecode), method definitions, etc. TypeLiteral uses the former, you may notice that it creates a brand new class, so the net effect is that your type parameter gets preserved. It's a nice workaround when you are really fighting against erasure.
You can't do it properly. Either use the raw type or use an unsafe cast.
List<String> myList = new ArrayList<String>();
Class<? extends List<String>> var = (Class<? extends List<String>>)myList.getClass();
You can write:
class ListString extends java.util.LinkedList<String> { }
Class<? extends java.util.List<String>> clazz = ListString.class;
However, given what Class represents, there isn't that much point. You are better off avoiding reflection wherever possible.
Related
Unfortunately, I don't know exactly how to correctly formulate my question.
But I think with an example it becomes clear what I want to know:
I want to create an instance of a generic class. But i don't know the types of the class yet.
So instead of this:
Foo<Type1, Type2> foo = new Foo<>();
i wanto to do something like this:
Class<? extends Type> type = getExpectedType();
Foo<Type1, type> foo = new Foo<>();
I've already tried this and it doesn't work. But is something similar possible?
That's impossible in Java, because type variables are not expressions. More concretely, Java has Type Erasure, meaning that these types don't actually exist during runtime, they are replaced by the compiler with up-casts and down-casts from, and to Object.
If you need to know the type at runtime, you must keep it in a field, and pass it to the constructor:
public Foo(Class<Type2> type2) {
...
this.type2 = type2;
}
then:
Class<? extends Type> type = getExpectedType();
Foo<Type1, ? extends Type> foo = new Foo<>(type);
How to safely cast Class<?> (returned by Class.forName()) to Class<Annotation> without issuing "Unchecked cast" warning?
private static Class<? extends Annotation> getAnnotation() throws ClassNotFoundException {
final Class<?> loadedClass = Class.forName("java.lang.annotation.Retention");
if (!Annotation.class.isAssignableFrom(loadedClass)) {
throw new IllegalStateException("#Retention is expected to be an annotation.");
}
#SuppressWarnings("unchecked")
final Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) loadedClass;
return annotationClass;
}
Multiple misconceptions need to be explained before delving into the answer.
You're using the wrong variance
final Class<Annotation> annotationClass = (Class<Annotation>) loadedClass;
This is actually illegal in any case. Try it:
Class<Number> n = Integer.class;
That won't compile.
Generics are invariant. It means that within the <>, you can't use a supertype as a standin for a subtype or vice versa.
Normal java (when <> are not involved) is covariant. Any subtype is a stand-in for one of its supertypes. This:
Number n = Integer.valueOf(5);
is perfectly legal java. But in generics world it isn't. If you want it to be, then, you have to opt into it: X extends Y is how you opt into covariance, and X super Y is how you opt into contravariance (contravariance is as if Integer i = new Number(); was legal - a SUPERtype can stand in for a subtype).
This is all because that's just how the universe ends up working out. If generics were naturally covariant, this would compile:
List<Integer> listOfInts = new ArrayList<>();
List<Number> listOfNums = listOfInts;
listOfNums.add(Double.valueOf(1.0));
int i = listOfInts.get(0);
but, follow along with your own eyes and you realize that code is a walking type violation. It shoves a non-integer into a list of integers. That's why opting into covariance or contravariances closes doors. If you opt into covariance, the add method is disabled *1:
List<? extends Number> list = new ArrayList<Integer>(); //legal
list.add(Integer.valueOf(5)); // will not compile
similarly, if you opt into contravariance, add works great, but get is disabled. 'disabled' in the type system sense: You can call it. But the expression list.get(i) would be of type Object:
List<? super Integer> list = new ArrayList<Number>(); // legal
list.add(Integer.valueOf(5)); // legal
Integer i = list.get(0); // won't compile
Object o = list.get(0); // this will.
With classes, where 'write' is not exactly clear, it's harder to see why Class<Annotation> c = SomeSpecificAnno.class; should fail to compile, but it does, so, that's important realization one.
Why are you using reflection here?
You can make class literals in java. This works great:
Class<? extends Number> c = Integer.class;
That's real java: You can stick .class at the end of any type and that will be an expression of type java.lang.Class, in fact, it's of type Class<TheExactThing>. So:
private static Class<? extends Annotation> getAnnotationType() {
return Retention.class;
}
works and compiles fantastically. I had to update the return type because as I explained above, returning the instance of j.l.Class that represents the Retention annotation for a method that is specced to return Class<Annotation> is as broken as returning an integer from a method that is specced to return a string.
The answer
If your code example is using java.lang.annotation.Retention as a stand-in, but your actual string here is a dynamic value that you do not know at compile time, the return Retention.class; option is off the table, then:
private static Class<? extends Annotation> getAnnotationType(String fqn) throws ClassNotFoundException {
return Class.forName(fqn).asSubclass(Annotation.class);
}
Again, do not use reflection unless there is no other way, and if you have the class in a string constant, generally you do not need reflection.
*1 ) You can call add, but only with a null literal; list.add(null); compiles, because null is trivially a valid value for any type. However, that's not particularly useful, of course.
Due to type erasure, generic type information is not accessible anymore at runtime. This means: Class<?> and Class<? extends Annotation> are indistinguishable at runtime - so you cannot do a runtime check to make the unchecked cast a checked one.
This means: you have to live with the warning (mind: a warning is not an error, it just means "this is problemattic, make sure you know what you're doing!).
Suppose I have an object Class<?> c. Is it possible to create a generic list with c as the generic parameter?
So something like this:
Class<?> c = doSomething();
List<c> list = new ArrayList<c>();
No that is impossible - at least your grammar will not compile. However, you may try to learn generics in Java, and see whether that helps your specific case, as this may be a A-B problem.
For example, this works:
<T> int yourFunction(List<T> items) {
T item = items.get(0);
// play with the item of type T, yeah!
}
For Class<? extend T> clazz, List<T> can be used for any instance created by clazz. For Class<? super T> clazz, List<T> should only contain instance that are compatibly with clazz.
For Class<?>, List<Object> is probably what you want. Any use of reflection, including Class is usually a mistake.
I have a Java question about generics. I declared a generic list:
List<? extends MyType> listOfMyType;
Then in some method I try instantiate and add items to that list:
listOfMyType = new ArrayList<MyType>();
listOfMyType.add(myTypeInstance);
Where myTypeInstance is just an object of type MyType; it won't compile. It says:
The method add(capture#3-of ? extends
MyType) in the type List<capture#3-of
? extends MyType> is not applicable
for the arguments (MyType)
Any idea?
You cannot do a "put" with extends . Look at Generics - Get and Put rule.
Consider:
class MySubType extends MyType {
}
List<MySubType> subtypeList = new ArrayList<MySubType>();
List<? extends MyType> list = subtypeList;
list.add(new MyType());
MySubType sub = subtypeList.get(0);
sub now contains a MyType which is very wrong.
You shouldn't need to use the wildcard capture syntax in your case, simply declaring
List<MyType> listOfMytype;
should be enough. If you want to know exactly why, the Java Generics Tutorial has more than you would ever want to know about the esoteric craziness of Java Generics. Page 20 addresses your specific case.
As for why add with the wildcard capture does not work, it is because the compiler can't determine exactly what subclass of MyType the list will be in every case, so the compiler emits an error.
There is a similar thread here:
How can elements be added to a wildcard generic collection?
To get an idea of how generics works check out this example:
List<SubFoo> sfoo = new ArrayList<SubFoo>();
List<Foo> foo;
List<? extends Foo> tmp;
tmp = sfoo;
foo = (List<Foo>) tmp;
The thing is, that wasn't designed for local/member variables, but for function signatures, that's why it's so ass-backwards.
I dont know if this will really help you, but this is something I had to use while calling a generic method of Spring Framework and wanting to return also a generic list:
public <T> List<T> findAll(String tableName,Class<?> table) {
String sql = "SELECT * FROM "+ tableName ;
List<?> entities = getSimpleJdbcTemplate().query(sql,
ParameterizedBeanPropertyRowMapper.newInstance(table));
return (List<T>) entities;
}
Seems the parametrization needs you to use the ? sign in the list to receive the results and then cast the list to the expected return type.
Iam still dazzled by generics...
I have a Java question about generics. I declared a generic list:
List<? extends MyType> listOfMyType;
Then in some method I try instantiate and add items to that list:
listOfMyType = new ArrayList<MyType>();
listOfMyType.add(myTypeInstance);
Where myTypeInstance is just an object of type MyType; it won't compile. It says:
The method add(capture#3-of ? extends
MyType) in the type List<capture#3-of
? extends MyType> is not applicable
for the arguments (MyType)
Any idea?
You cannot do a "put" with extends . Look at Generics - Get and Put rule.
Consider:
class MySubType extends MyType {
}
List<MySubType> subtypeList = new ArrayList<MySubType>();
List<? extends MyType> list = subtypeList;
list.add(new MyType());
MySubType sub = subtypeList.get(0);
sub now contains a MyType which is very wrong.
You shouldn't need to use the wildcard capture syntax in your case, simply declaring
List<MyType> listOfMytype;
should be enough. If you want to know exactly why, the Java Generics Tutorial has more than you would ever want to know about the esoteric craziness of Java Generics. Page 20 addresses your specific case.
As for why add with the wildcard capture does not work, it is because the compiler can't determine exactly what subclass of MyType the list will be in every case, so the compiler emits an error.
There is a similar thread here:
How can elements be added to a wildcard generic collection?
To get an idea of how generics works check out this example:
List<SubFoo> sfoo = new ArrayList<SubFoo>();
List<Foo> foo;
List<? extends Foo> tmp;
tmp = sfoo;
foo = (List<Foo>) tmp;
The thing is, that wasn't designed for local/member variables, but for function signatures, that's why it's so ass-backwards.
I dont know if this will really help you, but this is something I had to use while calling a generic method of Spring Framework and wanting to return also a generic list:
public <T> List<T> findAll(String tableName,Class<?> table) {
String sql = "SELECT * FROM "+ tableName ;
List<?> entities = getSimpleJdbcTemplate().query(sql,
ParameterizedBeanPropertyRowMapper.newInstance(table));
return (List<T>) entities;
}
Seems the parametrization needs you to use the ? sign in the list to receive the results and then cast the list to the expected return type.
Iam still dazzled by generics...