问题
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 String
s (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