结合原始类型和通用方法

时间:2012-06-13 03:10:22

标签: java generics generic-method raw-types

这是一个问题,第一个代码清单编译得很好(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[],尽管收到String[]作为其参数。

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

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

因此,它是一个参数化方法,其类型参数<T>与列表的参数无关(即<E>)。

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

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

5 个答案:

答案 0 :(得分:29)

这并不完全符合您的期望,但如果您以原始形式引用泛型类,则无法以任何方式使用泛型 作为实例成员。它也不限于通用方法,请查看:

 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节)M的类型是原始的类型,对应于与C对应的泛型声明中其类型的擦除。

答案 1 :(得分:8)

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

List l = new ArrayList<String>();

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

答案 2 :(得分:6)

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

  

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

     

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

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

  

原始类型的使用仅允许作为兼容性的让步   遗留代码。在编写后的代码中使用原始类型   将通用性引入Java编程语言是   强烈气馁。 有可能是Java的未来版本   编程语言将禁止使用原始类型

答案 3 :(得分:1)

可能有趣的是,这种行为可以解决&#34;。使用两个接口,一个基本非通用接口和一个通用接口。然后编译器知道基本非泛型接口的所有函数都不是通用的,并且会像这样处理它们。

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

通过接口继承进行搜索

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

public interface BaseInterface
{
    String getStringValue();
}

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

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

答案 4 :(得分:0)

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