8

Why is it possible to insert a String into a List<Integer> in the following code? I have a class which inserts numbers into a List of Integers:

public class Main {    
    public static void main(String[] args) {        
        List<Integer> list = new ArrayList<Integer>();
        list.add(2);
        list.add(3);
        list.add(4);

        Inserter inserter = new Inserter();
        inserter.insertValue(list);
        System.out.print(list);        
    }
}

Then I have a separate class which inserts a String into a List, with the numeric string value "42":

public class Inserter {    
    void insertValue(List list)
    {
        list.add(new String("42"));
    }
}

Why does the compiler not raise a compiler error, or the runtime throw a runtime exception such as *CastException, when I add the String to the List of Integer? Also, why does System.out.print(list) produce output like the following without throwing any exceptions?

[2, 3, 4, 42]

What is the fundamental reason that allows all this to happen?

Andy Brown
  • 18,961
  • 3
  • 52
  • 62
java_user
  • 929
  • 4
  • 16
  • 39
  • 3
    which compiler exception do you expect? – nano_nano Jan 30 '15 at 13:28
  • 1
    Where did you get the code from? My guess is that the point is to explain how arguments are passed in Java, observing that it's a *reference* to the list that is passed to the method (by value), not a copy of the list object. Or maybe it's meant to demonstrate raw types. It's not entirely clear where you'd expect an error though. Could you clarify the question please? – Jon Skeet Jan 30 '15 at 13:29
  • 2
    I think he's confused about how a `new String("42")` can be inserted into a value that was originally declared as `List`. – August Jan 30 '15 at 13:32
  • 1
    I expect something of *CastException or something because I try to put String object to List of Integers. – java_user Jan 30 '15 at 13:33
  • August - yes you are right – java_user Jan 30 '15 at 13:34

2 Answers2

9

This is probably an example to illustrate type erasure for generics (I recommend reading that link to fully understand this and the crucial role it plays in Java generics).

  1. list is declared as a List<Integer>
  2. When it is passed as an argument to listValue it is cast to a raw type
  3. From this point onwards in that method the program at run time has no knowledge that it was originally a "list of Integer", so it can insert any object with no exception - within the class declaration the type variable is erased to Object
  4. In the main method the print command simply calls toString on the list, which doesn't mind what it contains, and so it prints the elements including the string.

If you want to see an exception, try adding a line:

Integer myInt = list.get(3); // try to get the string

This will throw a ClassCastException as the compiler, during erasure, inserts casts where necessary to protect type safety.

Casts of parameterized types, such as List<Integer> to the raw type, such as List, give you a compiler warning to tell you that exactly this kind of problem might be about to happen. You can suppress that warning with @SuppressWarnings("unchecked") (or "rawtypes", if it is available to you). This is a good way of "acknowledging" that you are about to do something that will lead to an unchecked runtime exception, and also helps tell other future coders that something funky might be about to happen. E.g.:

void insertValue(@SuppressWarnings("unchecked") List list)
{
    list.add(new String("42"));
}
Community
  • 1
  • 1
Andy Brown
  • 18,961
  • 3
  • 52
  • 62
  • 1
    I would add that changing `void insertValue(List list)` to `void insertValue(List list)` would produce error and give compiler exception – ZqBany Jan 30 '15 at 13:44
  • @ZqBany. I'm not sure that's relevant here so I'm leaving it out. The point of that sample is to illustrate raw types and how they can sidestep type safety. Changing the method parameter to a parameterized type would defeat the object of the sample. If someone writes their own generic methods hopefully they never use raw types. – Andy Brown Jan 30 '15 at 13:52
  • I just think that this is behaviour expected by java_user, that compiler should throw exception at point of trying to add String to List of Integers. And adding this little piece of information how to get that behaviour could be useful, however of course this will be different methods and for some this could be irrevelant – ZqBany Jan 30 '15 at 14:11
0

The compiler will throw

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 234 at com.performance.Main.main(Main.java:26)

as you specified the type here ArrayList<Integer>(); and when you are adding a string list.add(new String("42"));

Santhosh
  • 8,181
  • 4
  • 29
  • 56
  • 1
    It's not clear what you are saying. The compiler doesn't throw an exception, the runtime does. Also as per the OP's code, no exception will be thrown at runtime. Why do you think one would be? – Andy Brown Jan 30 '15 at 14:16