未经检查的转换:将 'java.lang.Object[]' 转换为 'T[]'

3

我有以下方法:

public static <T> T[] toArray(Iterable<? extends T> iterable) {
  return (T[]) StreamSupport.stream(iterable.spliterator(), false).toArray();
}

我的IDE告诉我有一个未检查的转换。 总的来说,我理解错误消息,但我不明白为什么应该检查转换,因为可迭代对象(iterable)具有与我用于转换的相同类型。请问有人能解释一下吗?


1
嘿!为什么你在使用<? extends T>而不是简单的<T>呢? - akuzminykh
这难道不会使整个事情更具未来性吗? - lsiem
不,为什么要“未来可靠”呢?你知道? extends T是什么意思吗?如果你用Animal[] as = toArray(some Iterable<Dog>)或者Dog[] as = toArray(some Iterable<Animal>)或者Dog[] as = toArray(some Iterable<Dog>)调用这个方法会发生什么?哪一个能正常工作,为什么,而且这是我们期望的吗? - luk2302
1
这里没有所谓的“未来证明”。它的作用是允许某人执行Animal[] animals = toArray(dogs),其中dogs是一个List<Dog>。这样做可能有道理,但实际上并不需要,因为数组是协变的。所以如果你将其保留为T,你会得到一个Dog[],它也可以赋值给Animal[],因为它是协变的。因此,在这种情况下,通配符? extends T是不必要的。 - Zabuzard
我知道你在说什么。我一直在寻找其他的实现方式,Guava 也使用了它:链接。那里有什么区别呢? - lsiem
如果你想对T有更多的控制权,这样做没有任何问题,尤其是如果你不是非常需要它。 - Zabuzard
1个回答

6

我不明白为什么需要检查转换类型。

检查转换类型是指导致checkcast字节码指令的转换。例如:

String s = (String) someObject;

在泛型的情况下,无法插入checkcast指令,因为在代码的该点没有已知类型:由checkcast检查的类型静态地写入了字节码;但是你的方法必须适用于所有类型。因此,这里不能添加checkcast
警告是编译器表达“我不确定,但这里看起来有些可疑”的方式。事实上,确实存在一些问题,但在这段代码中问题并未出现。
假设你像下面这样调用了这个方法:
Integer[] ints = toArray(iterableOfInts);

实际上,会在调用处插入一个已检查的强制类型转换(由编译器插入):
Integer[] ints = (Integer[]) toArray(iterableOfInts);

这里可能会在运行时失败,因为Object[]无法转换为Integer[]。但是失败发生在这里,而不是在toArray方法中。
修复此问题的正确方法是提供一些内容来创建T[]
public static <T> T[] toArray(Iterable<? extends T> iterable, IntFunction<T[]> arraySupplier) {
  return StreamSupport.stream(iterable.spliterator(), false).toArray(arraySupplier);
}

Iterable<? extends T> 是一个适当的参数类型,因为它允许您创建一个超类型的数组:

Integer[] integers = toArray(iterableOfInts, Integer[]::new);
Object[] objects = toArray(iterableOfInts, Object[]::new);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接