-10

If we have a variable for the class of a generic class, like List, what type should it be?

List<Class> c1;

or

List<Class<?>> c2;  
Jankapunkt
  • 8,128
  • 4
  • 30
  • 59
irreputable
  • 44,725
  • 9
  • 65
  • 93
  • we'll see, @Daniel. what's your opinion? – irreputable Jul 05 '11 at 22:41
  • 9
    I have no opinion, only the **fact** that the **Java Language Specification** has an opinion on this: it strongly discourages the use of raw types. – Daniel Pryden Jul 05 '11 at 22:44
  • 1
    possible duplicate of [What is a raw type and why shouldn't we use it?](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – Tim Bender Jul 05 '11 at 23:46
  • 1
    I think my answer does involve facts, references and specific expertise, so I don't see why that question should be considered not constructive. Granted, it's very bad form to state up front that no answer will be accepted, and I can understand why that would trigger downvotes ... but closing as not constructive in the presence of several decent answers? – meriton Jul 06 '11 at 00:22
  • 2
    The question is not constructive, because of the way that it is asked. (And this also comes out in @irreputable's comments. He's more interested in arguing that Java's design is wrong than in the literal answer to the question ... IMO.) The fact that you've succeeded in answering it in a constructive way is a credit to you. – Stephen C Jul 06 '11 at 01:25
  • @Stephen There are self contradictions in JLS surrounding the issue, nothing wrong for me to point them out. It doesn't devalue my question which asks for the lesser of two evils. So which is your pick? `Class` or `Class>`. No need for explanation, just choose simply A or B. If you won't, I'll say that it's not uncommon on SO that an angry user votes to close a question because he doesn't know how to answer it. – irreputable Jul 06 '11 at 02:36
  • 2
    @irreputable - the real motivation of your question is to "argue the toss" over these (claimed) contradictions in the JLS. That is not constructive. That's the primary reason I voted to close ... not because I don't understand the question, or don't know how to answer it. – Stephen C Jul 06 '11 at 03:20
  • @Stephen It's not uncommon on SO that an angry user votes to close a question because he doesn't know how to answer it. – irreputable Jul 06 '11 at 03:23
  • 3
    @irreputable - If you really want people's opinions on whether there is a contradiction in the JLS, then you should write a Question that clearly sets out your case, and then asks people to offer their responses. But a better approach would be to start a blog where you can expound your theories. – Stephen C Jul 06 '11 at 03:34
  • 2
    Just because the asker is being argumentative and believes their question to be more subjective than it actually is, doesn't mean it's a completely useless, unconstructive question. If this question can prompt a response as in-depth as meriton's, it's not a worthless question. – Tyler Jul 06 '11 at 06:59

3 Answers3

10

The second one, because the first one uses the raw type instead of generics.

i.e. List is raw, but List<?> is generic, and you shouldn't mix-and-match between both raws and generics.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • but the class does represent the raw type. there is no class for `List>`; there is only class for `List` – irreputable Jul 05 '11 at 22:39
  • @irreputable: Confused at what you mean. – user541686 Jul 05 '11 at 22:41
  • what's the type of `List.class`? – irreputable Jul 05 '11 at 22:41
  • @irreputable: It's `List`, because generics *simply **do not exist*** at run-time. But I'm failing to see what that has to do with anything. The point is that you should either just use `Class` or use `Class>`, but **not** `Class` – user541686 Jul 05 '11 at 22:43
  • 3
    @irreputable: But omitting the type parameter entirely turns it into a raw type. And [JLS section 4.8](http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.8) says "The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. **It is possible that future versions of the Java programming language will disallow the use of raw types.**" – Daniel Pryden Jul 05 '11 at 22:43
  • @Daniel yet `java.lang.Object.getClass()` return type contains raw type. so don't believe them. – irreputable Jul 05 '11 at 22:46
  • @Mehrdad if we call `c2.getGenericInterfaces()` should it return a `Collection>` type? after all, the super interface of `List>` is `Collection>` – irreputable Jul 05 '11 at 22:49
  • 2
    @irreputable: No it doesn't. The [javadoc](http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#getClass()) says it returns `Class>`. – Daniel Pryden Jul 05 '11 at 22:52
  • @Daniel `|x|` is a raw type most times. – irreputable Jul 05 '11 at 22:53
  • 1
    @irreputable: How do you determine that? It sounds like you just disagree with the specification and came here looking for an argument. Well, you're wrong, but I'm not going to feed the troll any more. – Daniel Pryden Jul 05 '11 at 22:55
  • @irreputable: I think you were just looking at the wrong version of the javadocs. – user541686 Jul 05 '11 at 22:56
  • This answer boils down to, "You should use an unbound wildcard, because you shouldn't use raw types." Explain why. – erickson Jul 05 '11 at 22:59
  • 1
    @Mehrdad it's right there from Daniel's link – irreputable Jul 05 '11 at 22:59
  • @Daniel the javadoc of `java.lang.Object` and `java.lang.Class` is by all means part of the Java language spec. – irreputable Jul 05 '11 at 23:00
  • @erickson: Actually, that wasn't my point. My point was that he shouldn't *mix-and-match*, which is almost *always* a bad choice for anything, whether or not generics are a good idea. – user541686 Jul 05 '11 at 23:02
  • @irreputable: Huh? Daniel's link says `Class>` right there... – user541686 Jul 05 '11 at 23:02
  • 2
    @Mehrdad read two lines below: "**The actual result type is Class extends |X|>**". If the return type is indeed `Class>`, this shouldn't compile: `Class c = new Object().getClass()` because we cannot assign a `Class>` to `Class`. But it does compile, so the return type is **not** `Class>` – irreputable Jul 05 '11 at 23:09
  • @irreputable: Is there any warning during compilation? – user541686 Jul 05 '11 at 23:11
  • 1
    @irreputable: wait, it [doesn't compile](http://ideone.com/2GCTl) at all. – trutheality Jul 05 '11 at 23:34
  • @trutheality sorry should be `Class extends Object>`, which can't be assigned from `Class>` – irreputable Jul 06 '11 at 00:10
  • 1
    @irreputable: Why can't it? `?` is always an `Object`, isn't it? – user541686 Jul 06 '11 at 00:12
  • 1
    OK, my mistake again, actually `Class>` is just a short hand of `Class extends Object>`. The example should not use Object. try `Class extends String> c = new String().getClass();` – irreputable Jul 06 '11 at 00:24
  • @irreputable: Maybe it's just me, but I'm getting really confused at what you're trying to say... :( would you mind clarifying that? – user541686 Jul 06 '11 at 00:32
  • `Object.getClass()` return type is `Class extends |X|>`, which uses raw type `|X|`. We have an example in core Java that uses raw type in `Class`'s type parameter. Therefore the argument against `Class` because it contains raw type is refuted by Java spec. – irreputable Jul 06 '11 at 00:51
  • @irreputable: Ohh I see what you mean... yeah it's confusing me now too, no idea. – user541686 Jul 06 '11 at 00:56
10

Do you want to represent a runtime class, or a type? (The distinction being that List<String> and List<Integer> are different types, but share the same runtime class).

Type: Use something like a type token.

Runtime class: Since

    List<String> ls = new ArrayList<String>();
    Class<? extends List> c1 = ls.getClass();
    Class<List> c2 = List.class;

compiles, but

    Class<? extends List<?>> c3 = ls.getClass();
    Class<List<?>> c4 = List.class;

does not, I'd opt for using the raw type in the type expression. There really isn't any benefit from specifying the type argument to List because the class does not determine it, and using a wildcard type will require weird casting to get it to the proper type, for instance:

    Class<?> rawClass = List.class; // kludge: do not inline this variable, or compilation will fail
    Class<List<?>> classForBadAPI = (Class<List<?>>) rawClass;

Edit: Why it doesn't compile

Lifted from the comments:

why doesn't the 2nd code compile? the code makes perfect sense. Is it a design mistake in JDK? or is there valid reason for the choice?

List.class is of type Class<List>. Since List<?> and List are different types, Class<List<?>> and Class<List> are unrelated types, but the right-hand type of an assignment must be a subtype of the left-hand type. The getClass() case is analogous.

I would not blame the JDK, they only implemented the rules laid down in the language specification itself, in particular:

The type of a class literal, C.Class, where C is the name of a class, interface or array type, is Class<C>.

(source)

The type of a method invocation e.getClass(), where the expression e has the static type T, is Class<? extends |T|>.

(source)

We write |T| for the erasure of type T.

(source)

... and why is it defined like that?

Compiler knows the full generic type of e, but why does e.getClass() must return an erased type.

It's hard to give a definite answer to that, since the spec does not expand on the reasons for that definition. However, it might be because the runtime type might not be a subtype of the static type, a pathological situation that can arise by incorrect suppression of unchecked warnings (c.f heap pollution). By specifying that the return type only contains the erasure, the specification ensures that even in the presence of heap pollution, the class object returned by getClass() is an instance of the declared return type of getClass(). It also serves as a reminder that the runtime the programmer is about to access using the reflection API only thinks in terms of erased types.

meriton
  • 68,356
  • 14
  • 108
  • 175
  • why doesn't the 2nd code compile? the code makes perfect sense. Is it a design mistake in JDK? or is there valid reason for the choice? – irreputable Jul 05 '11 at 23:06
  • `List.class` is of type `Class`. Since `List>` and `List` are different types, `Class>` and `Class` are unrelated types, but the right-hand type of an assignment must be a subtype of the left-hand type. The `getClass()` case is analogous. – meriton Jul 05 '11 at 23:14
  • why does the compiler think the type of `ls.getClass()` is `Class< extends List>` but not `Class< extends List>`? It could choose the later, this is all compile time trick anyway, no run time type info needed. So why does the compiler refuse to choose the later, which will simplify client codes? – irreputable Jul 05 '11 at 23:29
  • @irreputable - you need to read the relevant parts of the JLS. That will explain what the compiler has to do, and help you answer questions about whether the compiler is doing the right thing. – Stephen C Jul 05 '11 at 23:52
  • 1
    ... and to make that easier, I have now cited the relevant rules from the spec. – meriton Jul 06 '11 at 00:01
  • I do question the spec. Compiler knows the full generic type of `e`, but why does `e.getClass()` must return an erased type. – irreputable Jul 06 '11 at 00:20
  • @meriton Are you and @Stephen C the same person? Or there is a bug? – irreputable Jul 06 '11 at 00:54
  • 2
    @irreputable - "I do question the spec". Yea well that's too bad. Because the Java language **is** specified that way, and that's what a Java compiler must implement. (If the Java designers had been given a "clean slate" in circa 2000 they would not designed Java generics like that. But there weren't: they had to take the "type erasure" approach for compatibility reasons.) – Stephen C Jul 06 '11 at 01:07
  • @irreputable - I am not @meriton. I've no idea why you would think that. – Stephen C Jul 06 '11 at 01:10
  • From the context I thought the #4 comment was from meriton but the system give it a wrong signature. – irreputable Jul 06 '11 at 02:24
  • @meriton When I asked why it doesn't compile, I was asking for the rationale behind it. We can easily write a `static Class extends T> getClass(T obj)` utility method, so that `Class extends List>> c3 = getClass(ls);` compiles, therefore your 2nd example is severely weakened as an argument against `Class>`. - Unless, we know the rationale behind the type choice of `Object.getClass()`, then it'll probably shows that my static `getClass()` isn't appropriate either. You mentioned that the class object doesn't know the `T` of the `List`, but we are talking about `List>` here. – irreputable Jul 06 '11 at 02:30
  • Duh, why do you think I wrote the third code snippet? Yes, that's the implementation of that utility method ... factoring it out in a method does not lessen the disadvantage, because callers have to know of this method's existence, or reinvent the kludge it contains. Yes, it may not be a terrible disadvantage, but since there is no compelling advantage it still settles the matter, doesn't it? Or can you come up with any advantage to using `Class>`? – meriton Jul 06 '11 at 12:47
5
Class<? extends List<?>>

since List itself is an interface here, this might be a better option.

user541686
  • 205,094
  • 128
  • 528
  • 886
Kal
  • 24,724
  • 7
  • 65
  • 65
  • no, I'm talking about the class of `List`, not any subclasses – irreputable Jul 05 '11 at 22:40
  • 1
    There is no "class of `List`" because `List` is an interface, not a class (though I grant you it's misleading that `List.class` is a valid expression and there is a file called `List.class` in rt.jar) – Tyler Jul 06 '11 at 06:58