3

I'm trying to understand generics, but I block at wildcard bounds and casts. For instance:

List<? extends Number> l = new ArrayList<>();
List<PositiveInteger> p = new ArrayList<PositiveInteger>();
p.add(new PositiveInteger(10));
l = p;
NegativeInteger ni = (NegativeInteger) l.get(0);// runtime java.lang.ClassCastException:
System.out.println(ni);

NegativeNumber and PositiveNumber both extend Number. So the question is why there is no warning at compile-time when casting the element from the list into a NegativeNumber ? Is there a way to prevent this type of exception ?

Anisotrop
  • 43
  • 5

3 Answers3

0

When you define the Generic type using wildcards like <? extends Number> you should not cast the objects into the subclasses of Number because the generic type is saying that you will get an instance of subclasses of Number. Similar to this chunk of code:

Number n = new PositiveInteger(10);
NegativeInteger  ni = (NegativeInteger ) n;
Alper
  • 571
  • 5
  • 15
0

As soon as you're casting, you tell the compiler "I know what I'm doing", so it won't complain as long as it's possible to cast the object (NegativeNumber extends Number -> ok).

If you really need to cast (which you want to avoid; generics is all about compile-time safty and avoiding "unsafe" casts), you could check it first like this:

Number number =  l.get(0);
if (number instanceof NegativeInteger){
    NegativeInteger ni = (NegativeInteger) l.get(0);
    ...
}
Puce
  • 37,247
  • 13
  • 80
  • 152
0

well, the same thing will happen if you'll do:

Object a;
Integer b = new Integer(5);
a=b
Double c = (Double)a;

ClassCastException happend because you are trying to convert a class to another class that isn't the parent/Child class

as to the question about preventing it:

the compiler don't know what will be in l in run time. but you can use List<PositiveInteger> instead of wildcard to tell the compiler what is going to be in l. if you have to use List<? extends Number> you can create a method that tries to cast the object, that way you can tell if the object is List<PositiveInteger> or List<NegativeInteger> like here:

if(l instanceof List<NegativeInteger>){
   //Do something
}

so if i change your code accordingly:

List<? extends Number> l = new ArrayList<>();
List<PositiveInteger> p = new ArrayList<PositiveInteger>();
p.add(new PositiveInteger(10));
l = p;
Object ni = l.get(0);
if(ni instanceof NegativeInteger){
   NegativeInteger tmp = (NegativeInteger)ni;
   System.out.println(ni);
}
Community
  • 1
  • 1
No Idea For Name
  • 11,411
  • 10
  • 42
  • 70
  • The use of instanceof was something I wanted to avoid. It seems puzzling to me that there is no warning when casting even in the simple form like your first code snippet. – Anisotrop Nov 11 '14 at 12:21
  • @Fram why do you want to avoid instaceof? i use it all the time. it's a lot better then catching exception on cast all the time – No Idea For Name Nov 11 '14 at 12:28