有两个节目 为什么第一个代码代码有效?我希望它在访问元素时抛出一个运行时异常,因为添加了String而不是Integer
类似地.. 第二个代码是在访问元素时抛出运行时异常,尽管它能够轻松地在arrayList中添加Integer,尽管它声明它可以保存String。
在这两个代码中,我们成功添加了不同的数据类型,但访问元素时似乎出现了问题
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);
}
}
答案 0 :(得分:3)
由于type erasure,您可以在两种情况下添加元素。在运行时,该类并不知道它被声明为$details = Details::with(['header'=>function($query) use ($id){
$query->where('customer_id', $id);
}])->get()
,只是它被声明为new ArrayList<String>()
。
在new ArrayList()
的情况下,方法的编译时重载决策起作用。 println
是PrintStream,System.out
有几个重载。其中包括:
println
Java编译器将选择最具体的那些。在...
void println(Object x);
void println(String x);
情况下,它是第一个,在ArrayList<Integer>
情况下,它是第二个。一旦它这样做,作为类型擦除处理的一部分,它会将原始ArrayList<String>
调用的Object结果转换为所需类型,但前提是必须进行转换。
在ArrayList::get(int)
调用的情况下,不需要强制转换(ArrayList<Integer>
返回一个Object,这正是方法所期望的),因此javac省略了它。在ArrayList::get(int)
调用的情况下,是是必要的,因此javac会添加它。你看到的是什么:
ArrayList<String>
实际编译为:
System.out.println(arrayList.get(0));
但元素不是String,而这是导致ClassCastException的原因。
答案 1 :(得分:1)
泛型仅在编译期间存在。它们在编译后从二进制代码中删除,这称为“类型擦除”。这主要是出于向后兼容的原因。
如果编译时没有警告且没有手动编译,则无法滥用泛型,因为这会导致编译器错误和一些警告。
当您声明您希望功能ArrayList
时,没有任何泛型指示。您禁用所有编译时检查。你应该对此发出警告。在这种情况下使用泛型参数的任何地方都只接受Object
。
但是当你使用get()
访问对象时,会发生一些隐藏的事情,一个演员。
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);
最后一行被重写为:
String s = (String) list.get(0);
如果列表没有返回String
类型的内容,则会失败。
当您仅在列表中使用泛型时,它将只有String
(或您的代码将无法编译)。但是,由于您已将非String
对象放入列表中,因此对强制转换的类型检查将失败。