Ben*_* I. 6 java arrays generics class
当我遇到一个小小的谜时,我正在回答一个不同的问题.类定义(稍微修改自原始提问者)在这里:
public class Playground<T>{
private int pos;
private final int size;
private T[] arrayOfItems;
public Playground(int size){
this.size = size;
pos = 0;
arrayOfItems = (T[]) new Object[size];
}
public void addItem(T item) {
arrayOfItems[pos] = item;
pos++;
}
public void displayItems() {
for(int i = 0;i<pos;i++){
System.out.println(arrayOfItems[i]);
}
}
public T[] returnItems() {
return (T[]) arrayOfItems;
}
}
Run Code Online (Sandbox Code Playgroud)
主要是,然后我们创建一个新的Playground,Playground<String> animals = new Playground<String>(5);并在其中放入一些动物字符串.(狗,猫等).
神秘之处在于:
Object[] s = animals.returnItems();
for(int i=0; i < s.length; i++) {
System.out.println(s[i]);
}
Run Code Online (Sandbox Code Playgroud)
但这会ClassCastException 在for循环声明中创建一个.
for(int i=0; i < animals.returnItems().length; i++) {
System.out.println(animals.returnItems()[i]);
}
Run Code Online (Sandbox Code Playgroud)
两个Object[]S和String[]■找长度变量.为什么在循环声明中使用访问器方法会导致异常?
有一个ClassCastException- 无法转换Object[]为String[]- 的原因是编译器在使用泛型时所做的事情.调用时returnItems(),编译器会插入一个强制转换String[],因为returnItems返回T[].编译时键入擦除意味着它返回一个Object[],但由于T在String这里,编译器插入一个强制转换String[].但是原始对象arrayOfItems不是a String[],它是a Object[],所以演员表失败了.
这应该在编译期间导致"未经检查的强制转换"警告,从而Object[]到T[].
您需要做的是遵循如何在Java中创建通用数组中的建议?在创建通用数组.
Class<T>在你的构造函数中接受a ,这样你就可以从一开始就调用Array.newInstance并得到一个T[].
@SuppressWarnings("unchecked") // This suppression is safe.
public Playground(int size, Class<T> clazz){
this.size = size;
pos = 0;
arrayOfItems = (T[]) Array.newInstance(clazz, size);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以animals通过传递创建String.class:
Playground<String> animals = new Playground<String>(5, String.class);
Run Code Online (Sandbox Code Playgroud)
更新
以下是关于为什么第一个示例Object[]在第二个示例不起作用时工作(分配给a )的原因(length直接在方法的返回类型上访问字段)的合理解释returnItems().
第一个例子
Object[] s = animals.returnItems();
for(int i=0; i < s.length;i++) {
System.out.println(s[i]);
}
Run Code Online (Sandbox Code Playgroud)
的JLS,第5.2节,描述了"作业上下文"支配从分配给一个变量的表达式的值时会发生什么.
在分配上下文中转换可能产生的唯一例外是:
- ClassCastException如果在应用了上述转换之后,结果值是一个对象,它不是变量类型的擦除(第4.6节)的子类或子接口的实例.
这种情况只能由于堆污染而产生(§4.12.2).在实践中,当擦除类型的字段或方法的擦除返回类型与其未擦除类型不同时,实现仅需要在访问参数化类型的对象的字段或方法时执行强制转换.
...
编译器不需要在String[]此处插入强制转换.当length稍后访问该字段时,该变量已经是类型Object[],因此这里没有问题.
第二个例子
for(int i=0; i < animals.returnItems().length;i++) {
System.out.println(animals.returnItems()[i]);
}
Run Code Online (Sandbox Code Playgroud)
在ClassCastException这里似乎不是依赖于for循环; 通过简单的长度打印将发生此错误:
System.out.println(animals.returnItems().length);
Run Code Online (Sandbox Code Playgroud)
这是一个字段访问表达式,由JLS第15.11.1节涵盖.
[T]标识符在类型T中命名单个可访问的成员字段,字段访问表达式的类型是捕获转换后的成员字段的类型(第5.1.10节).
捕获转换将类型捕获为String[].编译器必须将强制转换插入到String[]此处,原因与插入方法调用的强制转换相同 - 字段或方法可能仅存在于捕获的类型上.
因为类型arrayOfItems确实是Object[],演员表失败了.
如上所述,创建通用数组可以Array.newInstance解决此问题,因为String[]正在创建实际数据.通过该更改,插入的强制转换仍然存在,但这次成功.