fromSPb
fromSPb

Reputation: 313

Why is it possible to get back an object of "incorrect-type" from the parametrized List in Java?

Here's a code snippet:

import java.util.*;
class Test
{
    public static void main(String[] args)
    {
        List<Integer> list = new ArrayList<>();
        addToList(list);
        Integer i = list.get(0); //#1 fails at run-time
        String s = list.get(0); //#2 fails at compile-time
        list.get(0); //#3 works fine
        System.out.println(list.get(0)); //#4 works fine, prints "string"
    }
    static void addToList(List list){
        list.add("string");
    }
}

I understand why is it possible to insert an object of String class in parametrized List.

It seems like I understand why code marked with #1 and #2 fails.

But why do #3 and #4 work? As far as I understand, the compiler adds appropriate casts after type-erasure, so when I call list.get(0), this method should return an Object previously casted to Integer. So why there is no ClassCastException occures at #3 and #4 at run-time?

Upvotes: 21

Views: 927

Answers (5)

Jatin
Jatin

Reputation: 31744

If you look at ArrayList#get method. It is this:

public E get(int index) {
   //body
}

But at runtime it is actually:

public Object get(int index) {
       //body
}

So when you do Integer i = list.get(0); The compiler converts it to:

Integer i = (Integer)list.get(0);

Now at runtime, list.get(0) returns an Object type (which is actually String). It now tries converting String => Integer and it fails.

3

Because it is just:

list.get(0)

The compiler does add typecasting to anything. So it is just, list.get(0).

4

System.out.println(list.get(0));

list.get(0) returns an Object type. So public void println(Object x) method of the PrintStream gets called.

Remember println(Object x) gets called and not println(String x).

Upvotes: 5

uberwach
uberwach

Reputation: 1109

4: The overload System.out.println(Object) is being called, since Integer <=_T Object (read: Integer is-a Object). Note that list.get(int) returns an Object in run-time since the type parameter gets erased. Now read

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

that tells you "Insert type casts if necessary to preserve type safety.", since a type cast is not necessary from Object to Object the ClassCastException cannot result.

For the same reason there is no type cast at 3, however side effects of the method call could happen of which List.get has none.

Upvotes: 2

Hoopje
Hoopje

Reputation: 12932

First the reason why you can add a string to a List<Integer>. In the method

static void addToList(List list){

you use a raw type. Raw types exist purely for compatibility with older Java versions and should not be used in new code. Within the addToList method the Java compiler does not know that list should only contain integers, and therefore it doesn't complain when a String is added to it.

As for the different behavior of you two statements. Integer i = list.get(0) does not fail at compile time, because Java thinks that list only contains Integers. Only at runtime it turns out that the first element of list is not an Integer, and therefore you get a ClassCastException.

String s = list.get(0) fails at compile time because the Java compiler assumes that list only contains Integers, and so it assumes you try to assign an Integer to a String reference.

Just list.get(0) does not store the result of the method call. So neither at compile time nor at run time there is any reason for a failure.

Finally, System.out.println(list.get(0)) work because System.out is a PrintStream and has a println(Object) method, which can be called with an Integer argument.

Upvotes: 8

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726809

The #3 works because the object returned by get(int) is ignored. Whatever is stored at position 0 is returned, but since there is no cast, no error happens.

The #4 works fine for the same reason: the object produced by get(0) is treated like java.lang.Object subclass in println, because toString is called. Since toString() is available for all Java objects, the call completes without an error.

Upvotes: 22

The casts are applied to the return type of get, not to the add that's introducing the pollution. Otherwise, you'd be getting either a compile-time error or an exception at that point, since you can't cast a String to an Integer.

Upvotes: 1

Related Questions