Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am puzzled by generics. You can declare a field like:

Class<Collection<String>> clazz = ...

It seems logical that you could assign this field with:

Class<Collection<String>> clazz = Collection<String>.class;

However, this generates an error:

Syntax error on token ">", void expected after this token

So it looks like the .class operator does not work with generics. So I tried:

  class A<S> { }
  class B extends A<String> { }
  Class<A<String>> c = B.class;

Also does not work, generates:

Type mismatch: cannot convert from Class<Test.StringCollection> to Class<Collection<String>>

Now, I really fail to see why this should not work. I know generic types are not reified, but in both cases it seems to be fully type safe without having access to runtime generic types. Anybody an idea?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
99 views
Welcome To Ask or Share your Answers For Others

1 Answer

Generics are invariant.

Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>

Depending on what it is that you need, you may be able to use wildcards.

Class<? extends Number> klazz = Integer.class; // FINE!

Or perhaps you need something like this:

Class<List<String>> klazz =
   (Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
//   Class<capture#1-of ? extends ArrayList> to Class<List<String>>

As for the non-reified at run-time case, you seem to have a good grasp, but here's a quote anyway, from the Java Tutorials on Generics, The Fine Print: A Generic Class is Shared by All Its Invocations:

What does the following code fragment print?

List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

You might be tempted to say false, but you'd be wrong. It prints true, because all instances of a generic class have the same run-time class, regardless of their actual type parameters.

That is, there's no such thing as List<String>.class or List<Integer>.class; there's only List.class.

This is also reflected in the JLS 15.8.2 Class Literals

A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a . and the token class.

Note the omission of any allowance for generic type parameters/arguments. Furthermore,

It is a compile time error if any of the following occur:

  • The named type is a type variable or a parameterized type, or an array whose element type is a type variable or parameterized type.

That is, this also doesn't compile:

void <T> test() {
    Class<?> klazz = T.class; // DOESN'T COMPILE!
    // Illegal class literal for the type parameter T
}

Basically you can't use generics with class literals, because it just doesn't make sense: they're non-reified.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...