我只是看在列表接口中定义的方法: <T> T[] toArray(T[] a)
,并且我有一个问题。 为什么通用? 由于这一事实,方法是不完整的类型安全。 下面的代码片段编译,但会导致ArrayStoreException
:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
String[] stringArray = list.toArray(new String[]{});
在我看来,如果指定者是不是通用的,把列表类型的参数,它会更好。
我已经写了玩具的例子,它是确定不用其他通用:
package test;
import java.util.Arrays;
public class TestGenerics<E> {
private Object[] elementData = new Object[10];
private int size = 0;
public void add(E e) {
elementData[size++] = e;
}
@SuppressWarnings("unchecked")
//I took this code from ArrayList but it is not generic
public E[] toArray(E[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (E[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public static void main(String[] args) {
TestGenerics<Integer> list = new TestGenerics<Integer>();
list.add(1);
list.add(2);
list.add(3);
//You don't have to do any casting
Integer[] n = new Integer[10];
n = list.toArray(n);
}
}
有为什么它宣布这样的原因吗?
Answer 1:
从的javadoc :
像toArray()方法,该方法充当基于收集基于阵列和API之间的桥梁。 此外,该方法允许在运行时类型的输出阵列的精确控制,并且可以在某些情况下,可以用来节省分配开销。
这意味着程序员是超过它应该是什么类型的数组的控制。
例如,对于您ArrayList<Integer>
,而不是一个Integer[]
数组,你可能需要一个Number[]
或Object[]
数组。
此外,该方法还检查传入的阵列。如果在具有所有元素的足够的空间的阵列通过,所述toArray
方法重新使用该阵列。 这意味着:
Integer[] myArray = new Integer[myList.size()];
myList.toArray(myArray);
要么
Integer[] myArray = myList.toArray(new Integer[myList.size()]);
具有比相同的效果
Integer[] myArray = myList.toArray(new Integer[0]);
注意,在旧版本的Java的使用后一种操作反射检查数组类型,然后动态构建正确类型的阵列。 通过使在第一位置正确大小的数组,反射并没有被用于分配的内部的新的数组toArray
方法。 这已不再的情况下,并且两个版本可以互换使用。
Answer 2:
它宣称一般这样就可以编写代码如
Integer[] intArray = list.toArray(new Integer[0]);
没有铸造阵列回来。
它宣布与以下注释:
@SuppressWarnings("unchecked")
换句话说,Java正在相信你在同类型的数组参数来传递,所以不会出现你的错误。
Answer 3:
为什么该方法具有此签名的原因是因为toArray
API早泛型:方法
public Object[] toArray(Object[] a)
已经出台,早在Java 1.2的。
的相应的通用替换Object
与T
已被引入作为100%向后兼容选项:
public <T> T[] toArray(T[] a)
更改签名通用让呼叫者避免投:Java 5之前,呼叫者需要做的是:
String[] arr = (String[])stringList.toArray(new String[stringList.size()]);
现在,他们可以不用铸造相同的呼叫:
String[] arr = stringList.toArray(new String[stringList.size()]);
编辑:
对于一个更“现代”的签名toArray
的方法是一对重载:
public <T> T[] toArray(Class<T> elementType)
public <T> T[] toArray(Class<T> elementType, int count)
这将提供一个更具表现力,同样多才多艺,替代目前的方法签名。 有一个高效实现这一点,也与Array.newInstance(Class<T>,int)
方法在适当位置。 以这种方式更改签名不会向后兼容,虽然。
Answer 4:
它是类型安全的-它不会导致一个ClassCastException
。 这一般是什么类型安全的手段。
ArrayStoreException
是不同的。 如果包括ArrayStoreException
在“不是类型安全”,然后在Java中所有的数组不是类型安全的。
您发布的代码也产生ArrayStoreException
。 试一试:
TestGenerics<Object> list = new TestGenerics<Object>();
list.add(1);
String[] n = new String[10];
list.toArray(n); // ArrayStoreException
事实上,这是根本不可能允许用户在他们想要得到的类型的数组传递,并在同一时间没有ArrayStoreException
。 因为接受某些类型的阵列的任何方法签名也允许亚型的阵列。
那么既然是无法避免ArrayStoreException
, 为什么不让它尽可能地通用? 这样用户可以使用一些不相关类型的数组,如果它们在某种程度上知道,所有的元素将是该类型的实例?
Answer 5:
我认为dasblinkenlight可能是正确的,这事做泛型化与现有的方法,并完全兼容是实现一种微妙的东西。
beny23的点也很不错-该方法应该接受的超类型E[]
有人可能会尝试
<T super E> T[] toArray(T[] a)
但Java不允许super
上一个类型变量,由于缺乏使用情况:)
(编辑:不,这不是一个很好的用例super
,见https://stackoverflow.com/a/2800425/2158288 )
Answer 6:
之所以这种方法,因为它主要是历史性的。
有通用类和数组类型之间的区别:通用类的类型参数是在运行时被擦除,数组的元素的类型是没有的。 因此,在运行时,JVM中看到没有任何区别List<Integer>
和List<String>
,但它确实看到之间的差异Integer[]
和String[]
这样做的原因不同的是,阵列就一直存在,从Java 1.0起,而其中仅在Java中加入1.5(在向后兼容的方式)泛型。
集合API是在Java 1.2的补充,引入泛型之前。 当时List
界面已经包含的方法
Object[] toArray(Object[] a);
(见1.2的Javadoc这个副本 )。 这是为了建立与用户指定的运行时类型的数组的唯一方式:参数a
充当类型令牌,即,它确定所返回的数组的运行时类型(注意,如果A
是的一个子类B
, A[]
被认为是一个亚型B[]
尽管List<A>
不是的子类型List<B>
当泛型Java 1.5中引入,许多现有的方法进行处理通用和toArray
方法成为
<T> T[] toArray(T[] a);
其中,类型擦除后,具有相同签名的原始的非通用的方法。
文章来源: Java List T[] toArray(T[] a) implementation