Generics at Runtime [duplicate]

旧城冷巷雨未停 提交于 2021-02-10 07:01:55

问题


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);

    }
}

回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/41439041/generics-at-runtime

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!