2

There two programs Why is the first code code working?I expected it to throw a Run time Exception while accessing the elements as String is added instead of Integer

Similarly.. The second code is throwing Run time Exception while accessing the element though it is able to add Integer in the arrayList comfortably despite declaring it to hold String.

In both the codes,We are successful in adding the different Data types,but the problems seems to appear while accessing elements

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {

        ArrayList<Integer> arrayList = new ArrayList<>();
        Test.addToList(arrayList);

        System.out.println(arrayList.get(0));
    }

    public static void addToList(ArrayList arrayList) {
        arrayList.add("i");

    }
}






import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {

        ArrayList<String> arrayList = new ArrayList<>();
        Test.addToList(arrayList);

        System.out.println(arrayList.get(0));
    }

    public static void addToList(ArrayList arrayList) {
        arrayList.add(1);

    }
}
gawi
  • 2,843
  • 4
  • 29
  • 44
Sriharsha g.r.v
  • 456
  • 5
  • 13
  • 1
    Why it's allowed to `add` is because of the function definition - the function you defined does not know what types `arrayList` expects (or should allow) - as for the runtime error: `String` is a class, so the `ArrayList` stores references (sort of like addresses) - if you try to access memory location 1 it does not contain a valid `String` object, so you get the runtime error – UnholySheep Jan 03 '17 at 08:16
  • You should be getting a `ClassCastException` while running second. – NewUser Jan 03 '17 at 08:16
  • 1
    @UnholySheep But I'm not sure your comment explains why the first would also not fail. – Tim Biegeleisen Jan 03 '17 at 08:26
  • I disagree with this dupe close. The linked question is overly broad, and in particular doesn't address why an `ArrayList` would function differently from an `ArrayList`, as it does in this question. – yshavit Jan 03 '17 at 08:39

2 Answers2

3

You can add elements in both cases due to type erasure. At run time, the class doesn't know it was declared as new ArrayList<String>(), just that it was declared as new ArrayList().

In the println case, the method's compile-time overload resolution comes into play. System.out is a PrintStream, which has several overloads for println. Among these are:

...
void println(Object x);
void println(String x);

The Java compiler will pick whichever of those is most specific. In the ArrayList<Integer> case it's the first one, and in the ArrayList<String> case it's the second. Once it does that, as part of the type erasure handling, it will cast the Object result of the raw ArrayList::get(int) call to the required type, but only if that cast is necessary.

In the case of the ArrayList<Integer> call, the cast is not necessary (ArrayList::get(int) returns an Object, which is exactly what the method expects), so javac omits it. In the case of the ArrayList<String> call, it is necessary, so javac adds it. What you see as:

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

is actually compiled to:

System.out.println((String) arrayList.get(0));

But the element isn't a String, and that's what results in the ClassCastException.

yshavit
  • 42,327
  • 7
  • 87
  • 124
1

Generics only exist during compilation. They are removed from the binarycode after compiling, this is called 'type erasure'. It is done mostly for backwards compatibility reasons.

If you compile without warnings and without manual casting, misuse of the generics is not possible, as it will lead to compiler errors and some warnings.

When you state you expect an ArrayList for your function, without any indication of generics. You disable all the compiletime checks. You should have gotten a warning for this. Any place where a generic parameter is used in that case, will just accept Object.

However when you access the objects using get(), something hiddens happens, a cast.

ArrayList<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);

The last line is rewritten to:

String s = (String) list.get(0);

This fails if the list does not return something of type String. When you only use generics on the list, it will only have Strings (or your code wouldn't compile). But as you have allowed a non-String object into the list, the type check of a cast will fail.

Thirler
  • 20,239
  • 14
  • 63
  • 92