[英]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. 我理解为什么可以在参数化列表中插入String类的对象。
It seems like I understand why code marked with #1
and #2
fails. 似乎我理解为什么标有#1
和#2
代码失败了。
But why do #3
and #4
work? 但为什么#3
和#4
有效呢? 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. 据我所知,编译器在类型擦除后添加了适当的强制类型,因此当我调用list.get(0)
,此方法应该返回一个先前已转换为Integer的Object。 So why there is no ClassCastException occures at #3 and #4 at run-time? 那么为什么在运行时没有ClassCastException出现在#3和#4?
The #3 works because the object returned by get(int)
is ignored. #3有效,因为忽略了get(int)
返回的对象。 Whatever is stored at position 0
is returned, but since there is no cast, no error happens. 返回存储在位置0
内容,但由于没有强制转换,因此不会发生错误。
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. 由于同样的原因,#4工作正常: get(0)
生成的对象被视为println
java.lang.Object
子类,因为调用了toString
。 Since toString()
is available for all Java objects, the call completes without an error. 由于toString()
可用于所有Java对象,因此调用完成时没有错误。
First the reason why you can add a string to a List<Integer>
. 首先,您可以将字符串添加到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. 原始类型纯粹是为了与较旧的Java版本兼容而存在,不应在新代码中使用。 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. 在addToList
方法中,Java编译器不知道该list
应该只包含整数,因此在向其添加String时它不会抱怨。
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 Integer
s. Integer i = list.get(0)
在编译时不会失败,因为Java认为该list
只包含Integer
。 Only at runtime it turns out that the first element of list
is not an Integer, and therefore you get a ClassCastException
. 只有在运行时才会发现list
的第一个元素不是Integer,因此你得到一个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. String s = list.get(0)
在编译时失败,因为Java编译器假定list
只包含整数,因此它假定您尝试将一个Integer分配给String引用。
Just list.get(0)
does not store the result of the method call. 只是list.get(0)
不存储方法调用的结果。 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. 最后, System.out.println(list.get(0))
工作,因为System.out
是一个PrintStream
并且有一个println(Object)
方法,可以使用Integer
参数调用该方法。
If you look at ArrayList#get
method. 如果你看一下ArrayList#get
方法。 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);
所以当你做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
). 现在,在运行时, list.get(0)
返回一个Object
类型(实际上是String
)。 It now tries converting String => Integer
and it fails. 它现在尝试转换String => Integer
并且它失败了。
3 3
Because it is just: 因为它只是:
list.get(0)
The compiler does add typecasting to anything. 编译器确实为任何事物添加了类型转换。 So it is just, list.get(0)
. 所以它只是, list.get(0)
。
4 4
System.out.println(list.get(0));
list.get(0)
returns an Object
type. list.get(0)
返回一个Object
类型。 So public void println(Object x)
method of the PrintStream gets called. 因此调用PrintStream的public void println(Object x)
方法。
Remember println(Object x) gets called and not println(String x) . 记住println(Object x)被调用而不是println(String x) 。
4: The overload System.out.println(Object) is being called, since Integer <=_T Object (read: Integer is-a Object). 4:正在调用重载System.out.println(Object),因为Integer <= _ T Object(读取:Integer是一个Object)。 Note that list.get(int) returns an Object in run-time since the type parameter gets erased. 请注意,list.get(int)在运行时返回一个Object,因为类型参数被擦除。 Now read 现在看了
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html 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. 它告诉你“如果需要,插入类型转换以保持类型安全。”,因为从Object到Object不需要类型转换,所以不能产生ClassCastException。
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. 出于同样的原因,在3处没有类型转换,但是方法调用的副作用可能发生在哪个List.get没有。
The casts are applied to the return type of get
, not to the add
that's introducing the pollution. 演员阵容适用于get
的返回类型,而不是引入污染的add
。 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
. 否则,您将在此时获得编译时错误或异常,因为您无法将String
转换为Integer
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.