In Java, how can I avoid raw types when calling getClass on an instance of a generic type?
Asked Answered
B

2

9

Suppose I have this in Java:

List<String> list = new ArrayList<String>();
list.getClass();

The type of the last expression is Class<? extends List>. I understand why, due to erasure, it cannot be Class<? extends List<String>>. But why can't it be Class<? extends List<?>>?

Is there no way for me to avoid both unchecked cast warnings and raw type warnings if I want to assign the result of this expression to a variable that somehow keeps the information that this class is actually some kind of List?

Class<? extends List> listClass = list.getClass(); // raw type warning
Class<? extends List<?>> listClass = (Class<? extends List<?>>) list.getClass(); // unchecked cast warning
Babin answered 13/9, 2013 at 9:31 Comment(1)
Mildly related: #18198822Wharfage
W
7

When generics were first introduced, getClass returned Class<? extends X>, where X was the static type of the expression on which it was called. This behavior led to unreasonable compilation issues, as reported in this Oracle bug. Here is that bug report's example:

The following program fragment fails to compile

void f(List<Integer> li, List<String> ls) {
    if (li.getClass() == ls.getClass())
  ;
}

because the intersection of Class<List<Integer>> and Class<List<String>> is empty.

This issue was resolved by widening the return type of getClass to be what it is now. From the documentation:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

This resolved the above issue but consequently led to the issue that your question points out. Not long after, another bug was reported, arguing the following:

I think the getClass() typing rule could be changed to Class<? extends wildcard(T)>

The wildcard operation is defined by: if T is parametrized, wildcard(T)=erasure(T)<?> else , wildcard(T)=T

JUSTIFICATION :

  1. This rule introduce a raw type. Raw type must ONLY be used to interact with legacy code.

  2. The new Rule introduce a wildcard. Relationship between parametrized type and wildcard are based on subtyping rules. Relationship between parametrized type and wildcard are based on raw type conversion.

This bug was not acted upon and remains open to this day, with the following counterarguments:

The proposal means that getClass() would return a Class<? extends ArrayList<?>> object, which is incompatible with other Class<? extends ArrayList<?>> objects. This is compatible with existing code like:

List<String> l = ...;
Class<? extends List> c = l.getClass();

because the new type of the RHS, Class<? extends List<?>>, is a subtype of Class<? extends List>.

A disadvantage of enriching Class's type argument is that it will break idiomatic use of Class.cast. Today, you can write:

List<Integer> x = ...;
Class<? extends List> cl = x.getClass();  
List<Integer> y = cl.cast(null);

and get a warning at cast(), because of the unchecked conversion from List to List<Integer>. But with the proposal, the analogous code doesn't compile:

List<Integer> x = ...;
Class<? extends List<?>> cl = x.getClass();
List<Integer> y = cl.cast(null);

because List<?> returned by cast() cannot be converted to List<Integer>. The only way to avoid the error is to cast cl.cast(..) up to List and suffer the unchecked conversion warning to List<Integer>. This is effectively what getClass() does already.

Overall, the proposal seems like a good idea, but it has moderate complexity and a fairly small payoff.

(abridged and with some typos corrected)

Wharfage answered 15/9, 2013 at 21:37 Comment(0)
B
-3

Being that List is an Interface, I am not sure it is possible to find out that the List interface is implemented for ArrayList. Found this Link here that might help.

I did mess around with this for a bit and found that...

    Class<?> d = list.getClass();
    d.equals(ArrayList.class);

but I am not sure if that is what your looking for...

Good Luck!

Bowse answered 14/9, 2013 at 2:41 Comment(3)
It has nothing to do with the fact that List is an interface. The ArrayList part is also irrelevant, as well as your example. I'm interested in the static return type of the getClass() method.Babin
Well forgive my ignorance, but if you are looking for the type, how do you expect anything other than ArrayList as that is the type that was instantiated? If you are looking to know if list implements List, wouldn't instanceof work? I do find myself very curious about what solution you come up with, if you could please keep us updated.Bowse
I am interested in the static type (i.e., the return type that the compiler can know) that getClass() returns when called on an instance of type List<String> (or List<?>, for that matter). The fact that it actually is an ArrayList is immaterial.Babin

© 2022 - 2024 — McMap. All rights reserved.