结合原始types和通用方法

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

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

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

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

我得到一个编译器错误,说String[]是必需的,但是find了Object[]

这意味着我的编译器将通用方法解释为返回Object[]尽pipe接收到String[]作为其参数。

我加倍检查了toArray(myArray)方法签名:

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

因此它是一个参数化方法,其types参数<T>与列表(即<E> )没有任何关系。

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

  • 有没有人有任何想法为什么这个代码不能编译?
  • 有没有人知道这种行为logging的任何参考?

这不完全是你所期望的,但是如果你以原始forms引用一个generics类,那么你将失去以任何方式使用generics的能力,例如成员。 它不限于通用方法,看看这个:

  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 )的相关部分:

未从其超类或超接口inheritance的原始typesC的构造函数(第8.8节),实例方法(第8.4节,第9.4节)或非静态字段(第8.3节)M的types是对应的原始types删除对应于C的generics声明中的types

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

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

将实际的typesparameter passing给非超类或超接口inheritance的原始types的非静态types成员是编译时错误。

基于上述和观察到的行为,我认为可以安全地说, 所有generics参数types都从原始types中删除。 当然,在非遗留代码中阻止使用原始types本身:

只允许使用原始types作为遗留代码兼容性的一个让步。 强烈build议不要在将通用性引入Java编程语言后编写的代码中使用原始types。 未来版本的Java编程语言可能会禁止使用原始types

当你不使用generics时,编译器把它当作一个原始types,因此每个generics都变成了Object ,所以你不能传递String[]因为它需要Object[]
所以这里是交易 – 如果你使用

 List l = new ArrayList<String>(); 

您正在使用原始types,其所有实例成员都被其擦除对象replace。 特别是出现在实例方法声明中的每个参数化types都被replace为其原始对象。 有关详细信息,请参阅JLS 4.8。

可能有趣的是,这种行为可以被“解决”。 使用两个接口,一个基本非通用接口和一个通用接口。 然后编译器知道基类非generics接口的所有function都不是通用的,并会像这样对待它们。

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

通过接口inheritance的说法

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

现在你可以做下面没有警告或问题:

 GenericInterface object = ...; String stringValue = object.getStringValue(); 

因为你的ArrayList是一个非参数化的列表,所以不能传入toArray()方法,只知道它存放Object,就是这样。 a.toArray()将始终返回一个Object []数组。 同样,如果你想指定它保存特定的stringtypes,你必须把它转换为(String[]) (其中包含所有的危险)。