为什么可以从 Java 中的参数化列表中取回 "incorrect-type" 的对象?
Why is it possible to get back an object of "incorrect-type" from the parametrized List in Java?
这是一个代码片段:
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");
}
}
我明白为什么可以在参数化列表中插入字符串 class 的对象。
看来我明白为什么标有 #1
和 #2
的代码会失败。
但为什么 #3
和 #4
有效?据我所知,编译器在类型擦除后添加了适当的强制转换,因此当我调用 list.get(0)
时,此方法应该 return 之前已强制转换为 Integer 的对象。那么为什么在 运行 时间的#3 和#4 没有发生 ClassCastException?
转换应用于 get
的 return 类型,而不是引入污染的 add
。否则,您将在此时遇到编译时错误或异常,因为您无法将 String
转换为 Integer
.
#3 起作用是因为 get(int)
返回的对象被忽略了。存储在位置 0
的任何内容都会返回,但由于没有转换,因此不会发生错误。
#4 出于同样的原因工作正常:get(0)
生成的对象被视为 println
中的 java.lang.Object
子类,因为 toString
被调用。由于 toString()
可用于所有 Java 对象,调用完成时没有错误。
首先是为什么可以将字符串添加到 List<Integer>
的原因。在方法
static void addToList(List list){
您使用 原始类型。原始类型的存在纯粹是为了与旧 Java 版本兼容,不应在新代码中使用。在 addToList
方法中,Java 编译器不知道 list
应该只包含整数,因此在向其添加字符串时它不会抱怨。
至于你们两个说法的不同行为。 Integer i = list.get(0)
不会在编译时失败,因为 Java 认为 list
只包含 Integer
。只有在 运行 时才发现 list
的第一个元素不是整数,因此你得到 ClassCastException
.
String s = list.get(0)
在编译时失败,因为 Java 编译器假定 list
仅包含整数,因此它假定您尝试将整数分配给字符串引用。
只是list.get(0)
不存储方法调用的结果。因此,无论是在编译时还是在 运行 时,都没有任何失败的原因。
最后,System.out.println(list.get(0))
起作用了,因为 System.out
是一个 PrintStream
并且有一个 println(Object)
方法,可以用 Integer
参数调用。
4:正在调用重载 System.out.println(Object),因为 Integer <=_T Object(阅读:Integer is-a Object)。请注意,list.get(int) returns 一个对象在 运行 时间内,因为类型参数被擦除。现在阅读
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
这告诉您 "Insert type casts if necessary to preserve type safety.",因为从对象到对象不需要类型转换,所以不会产生 ClassCastException。
出于同样的原因,在 3 处没有类型转换,但是 List.get 具有 none.
的方法调用可能会产生副作用
如果你看看ArrayList#get
方法。是这样的:
public E get(int index) {
//body
}
但在运行时实际上是:
public Object get(int index) {
//body
}
所以当你Integer i = list.get(0);
编译器将其转换为:
Integer i = (Integer)list.get(0);
现在在运行时,list.get(0)
returns 一个 Object
类型(实际上是 String
)。它现在尝试转换 String => Integer
但失败了。
3
因为它只是:
list.get(0)
编译器确实向任何内容添加了类型转换。所以它只是,list.get(0)
.
4
System.out.println(list.get(0));
list.get(0)
returns Object
类型。因此调用 PrintStream 的 public void println(Object x)
方法。
记得println(Object x) gets called and not println(String x).
这是一个代码片段:
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");
}
}
我明白为什么可以在参数化列表中插入字符串 class 的对象。
看来我明白为什么标有 #1
和 #2
的代码会失败。
但为什么 #3
和 #4
有效?据我所知,编译器在类型擦除后添加了适当的强制转换,因此当我调用 list.get(0)
时,此方法应该 return 之前已强制转换为 Integer 的对象。那么为什么在 运行 时间的#3 和#4 没有发生 ClassCastException?
转换应用于 get
的 return 类型,而不是引入污染的 add
。否则,您将在此时遇到编译时错误或异常,因为您无法将 String
转换为 Integer
.
#3 起作用是因为 get(int)
返回的对象被忽略了。存储在位置 0
的任何内容都会返回,但由于没有转换,因此不会发生错误。
#4 出于同样的原因工作正常:get(0)
生成的对象被视为 println
中的 java.lang.Object
子类,因为 toString
被调用。由于 toString()
可用于所有 Java 对象,调用完成时没有错误。
首先是为什么可以将字符串添加到 List<Integer>
的原因。在方法
static void addToList(List list){
您使用 原始类型。原始类型的存在纯粹是为了与旧 Java 版本兼容,不应在新代码中使用。在 addToList
方法中,Java 编译器不知道 list
应该只包含整数,因此在向其添加字符串时它不会抱怨。
至于你们两个说法的不同行为。 Integer i = list.get(0)
不会在编译时失败,因为 Java 认为 list
只包含 Integer
。只有在 运行 时才发现 list
的第一个元素不是整数,因此你得到 ClassCastException
.
String s = list.get(0)
在编译时失败,因为 Java 编译器假定 list
仅包含整数,因此它假定您尝试将整数分配给字符串引用。
只是list.get(0)
不存储方法调用的结果。因此,无论是在编译时还是在 运行 时,都没有任何失败的原因。
最后,System.out.println(list.get(0))
起作用了,因为 System.out
是一个 PrintStream
并且有一个 println(Object)
方法,可以用 Integer
参数调用。
4:正在调用重载 System.out.println(Object),因为 Integer <=_T Object(阅读:Integer is-a Object)。请注意,list.get(int) returns 一个对象在 运行 时间内,因为类型参数被擦除。现在阅读
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
这告诉您 "Insert type casts if necessary to preserve type safety.",因为从对象到对象不需要类型转换,所以不会产生 ClassCastException。
出于同样的原因,在 3 处没有类型转换,但是 List.get 具有 none.
的方法调用可能会产生副作用如果你看看ArrayList#get
方法。是这样的:
public E get(int index) {
//body
}
但在运行时实际上是:
public Object get(int index) {
//body
}
所以当你Integer i = list.get(0);
编译器将其转换为:
Integer i = (Integer)list.get(0);
现在在运行时,list.get(0)
returns 一个 Object
类型(实际上是 String
)。它现在尝试转换 String => Integer
但失败了。
3
因为它只是:
list.get(0)
编译器确实向任何内容添加了类型转换。所以它只是,list.get(0)
.
4
System.out.println(list.get(0));
list.get(0)
returns Object
类型。因此调用 PrintStream 的 public void println(Object x)
方法。
记得println(Object x) gets called and not println(String x).