28

这是一个问题,第一个代码清单编译得很好(JDK 1.6 | JDK 1.7):

ArrayList<String> a = new ArrayList<String>();
String[] s = a.toArray(new String[0]);

但是,如果我将List引用声明为原始类型:

ArrayList a = new ArrayList();
String[] s = a.toArray(new String[0]);

我收到一个编译器错误,说它String[]是必需的,但Object[]被发现了。

这意味着我的编译器将泛型方法解释为返回Object[],尽管它接收到 aString[]作为其参数。

我仔细检查了toArray(myArray)方法签名:

<T> T[] toArray(T[] a);

因此,它是一种参数化方法,其类型参数与 List(即)的类型参数<T>没有任何关系。<E>

我不知道在这里使用原始类型如何影响使用独立类型参数的参数化方法的评估。

  • 有谁知道为什么这段代码不能编译?
  • 有人知道记录此行为的任何参考吗?
4

5 回答 5

33

这并不完全符合您的期望,但是如果您以原始形式引用泛型类,您将失去以任何方式对实例成员使用泛型的能力。它也不限于泛型方法,请查看:

 public class MyContainer<T> {

     public List<String> strings() {
         return Arrays.asList("a", "b");
     }
 }

 MyContainer container = new MyContainer<Integer>();
 List<String> strings = container.strings(); //gives unchecked warning!

这是 JLS ( 4.8 ) 的相关部分:

未从其超类或超接口继承的原始类型 C 的构造函数(第 8.8 节)、实例方法(第 8.4 节、第 9.4 节)或非静态字段(第 8.3 节)的类型是对应的原始类型在对应于 C 的泛型声明中擦除其类型。

于 2012-06-13T03:37:43.093 回答
8

当您不使用泛型时,编译器会将其视为原始类型,因此每个泛型类型都会变成Object,因此您无法通过String[],因为它需要Object[]
所以这是交易 - 如果您使用

List l = new ArrayList<String>();

您正在使用原始类型,并且它的所有实例成员都被其擦除对应物替换。特别是出现在实例方法声明中的每个参数化类型都被其原始对应物替换。有关详细信息,请参阅 JLS 4.8。

于 2012-06-13T03:21:04.590 回答
6

这是我在规范中找到的最接近描述这种观察到的行为的描述:

未从其超类或超接口继承的原始类型 C 的构造函数(第 8.8 节)、实例方法(第 8.8 节、第 9.4 节)或非静态字段(第 8.3 节)M 的类型是其类型的擦除在对应于 C 的泛型声明中。原始类型 C 的静态成员的类型与其在对应于 C 的泛型声明中的类型相同。

将实际类型参数传递给未从其超类或超接口继承的原始类型的非静态类型成员是编译时错误。

基于上述和观察到的行为,我认为可以肯定地说所有泛型参数类型都从原始类型中删除。当然,在非遗留代码中不鼓励使用原始类型本身:

仅允许使用原始类型作为对遗留代码兼容性的让步。强烈反对在将泛型引入 Java 编程语言之后编写的代码中使用原始类型。Java 编程语言的未来版本可能不允许使用原始类型

于 2012-06-13T03:57:08.743 回答
1

有趣的是,这种行为可以“解决”。使用两个接口,一个基本的非泛型接口和一个泛型接口。然后编译器知道基础非泛型接口的所有函数都不是泛型的,会这样对待它们。

如果使用流和函数链接,这种行为非常烦人,因此我像下面这样解决它。

通过接口继承的解决方案

public interface GenericInterface<X extends Y> extends BaseInterface
{
    X getValue();
}

public interface BaseInterface
{
    String getStringValue();
}

现在您可以在没有警告或问题的情况下执行以下操作:

GenericInterface object = ...;
String stringValue = object.getStringValue();
于 2017-05-03T09:54:07.000 回答
0

不能将类型参数传递给 toArray() 方法,因为您的 ArrayList 是一个非参数化列表,它只知道它包含对象,仅此而已。a.toArray()将始终返回一个 Object[] 数组。同样,(String[])如果要指定它包含特定的 String 类型,则必须将其强制转换为(其中包含所有危险)。

于 2012-06-13T03:27:22.283 回答