13

Given this code:

List<Integer> ints = new ArrayList<Integer>();

// Type mismatch: 
// cannot convert from Class<capture#1-of ? extends List> to Class<List<Integer>>       
Class<List<Integer>> typeTry1 = ints.getClass(); 

// Type safety: 
// Unchecked cast from Class<capture#2-of ? extends List> to Class<List<Integer>>
Class<List<Integer>> typeTry2 = (Class<List<Integer>>) ints.getClass();

// List is a raw type. References to generic type List<E> should be parameterized
Class<? extends List> typeTry3 = ints.getClass(); 

Is there a way to get the Class of a List<Integer> without an error or warning? I can suppress the warnings easily enough, but if Java requires me to suppress a warning for this valid code, I am very disappoint.

On the other hand, if warning suppression is the only way, what is the safest to suppress?

Rob Kielty
  • 7,464
  • 7
  • 35
  • 50
cqcallaw
  • 1,283
  • 3
  • 17
  • 28
  • Apparently there is no warning free way because of erasure. http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens – user845279 Jun 16 '12 at 04:43
  • There is really no point in using a concrete generic type for the Class object reference, because that information is not there in the object being referenced, so in my Opinion `Class extends List>` or `Class extends List>>` are the best types for the variable. I was surprised though that I couldn't find a warning-free cast to the wildcarded target type... However, as Class objects are mostly used for reflection anyway, you might be fine using `Class>`, which works without warnings – Silly Freak Feb 11 '14 at 14:46

2 Answers2

6

This is a real Catch-22 in Java.

The compiler warns you if you don't add a generic type to List:

// WARN: List is not typed
Class<? extends List> typeTry3 = ints.getClass();

That's because, in most cases, it's really best to type your Lists.

However, because of type erasure, there's no way for Java to figure out the generic type of List at runtime, and the compiler knows that. Therefore, there is no method on Class that will returned a typed object:

// ERROR: ints.getClass() doesn't return a Class<List<Integer>>, it returns a Class<List>
Class<? extends List<? extends Integer>> var = ints.getClass();

So you must cast it to a typed list. However, as you know, since there is no runtime type checking, Java warns you about any casts to a typed variable:

// WARN: Casting to typed List
Class<List<Integer>> typeTry2 = (Class<List<Integer>>) ints.getClass();

Any attempt to get around this is essentially a means of confusing the compiler, and will inevitably be convoluted.

Your best bet then is to go with Option B:

On the other hand, if warning suppression is the only way, what is the safest to suppress?

The safest way to suppress this warning is to make your @SuppressWarnings("unchecked") annotation as localized as possible. Put them above each individual unchecked cast. That way it's absolutely clear who's causing the warning.

Tim Pote
  • 24,528
  • 6
  • 58
  • 63
4

One way to do this would be to create your own class. I am not sure if this would meet your needs.

I am not really sure what you are trying to achieve. So perhaps wildcards is the answer.

import java.util.*;

class Main
{
      public static void main(String[] args)
      {
            List<Integer> list = new ArrayList<Integer>();
            Class<?> clazz = list.getClass();
            System.out.println(clazz) ;
      }
}
gobernador
  • 5,271
  • 3
  • 28
  • 50
emory
  • 10,259
  • 1
  • 28
  • 55
  • It seems to me like a faire solution to tell the compiler: "I know that I will get a class of _something_, please give it to me without complains, even though _you_ (the compiler) may be unable to infer/keep the type of this _something_". – Yann-Gaël Guéhéneuc Apr 14 '13 at 13:00