如果实例没有分配泛型类型,则针对每个循环问题进行泛型

时间:2010-07-23 07:22:22

标签: java generics

有人可以向我解释为什么明确需要为ForEachLoop实例分配泛型类型?

为什么编译器会抱怨: 类型不匹配:无法从元素类型Object转换为String

JDK 1.5.0_09

import java.util.ArrayList;
import java.util.Collection;

public class ForEachLoop<T> {

public static void main(String[] args) {

    // Non functional version
    ForEachLoop f = new ForEachLoop(); 

    // Functional version
    //ForEachLoop<Integer> f = new ForEachLoop();

            // Type mismatch: cannot convert from element type Object to String
    for(String a : f.getStrings()) {
        System.out.println(a);
    }
}

public Collection<String> getStrings() {
    Collection<String> strings = new ArrayList<String>();
    strings.add("Hello");
    return strings;
}

} 

5 个答案:

答案 0 :(得分:10)

这是一个相当常见的错误:

ForEachLoop f = new ForEachLoop(); 

应该是

ForEachLoop<Something> f = new ForEachLoop<Something>();

如果您使用原始类型(您不应该使用),编译器将清除该实例的所有通用信息,即使它不是类型参数T,以使其与1.5之前的代码兼容。

如果您正在为Java 1.4或更低版本编写,则只使用原始类型,在这种情况下,您不应该使用任何泛型。在字节码级别,该方法在类型擦除后返回Collection(raw)。通常,如果实例具有泛型类型集,当您尝试对集合执行get时,编译器将使用通用信息来确定它应该返回一个String,然后在字节码级别它会自动转换它从Collection到String接收的对象(因为它保证是一个String)。但是如果使用原始类型,编译器将忽略所有通用信息,并且不会再为您自动转换对象。

修改:在原始类型部分中有以下内容:

  

上述规则的另一个含义   是原始的通用内部类   type本身只能用作raw   类型:

class Outer<T>{
  class Inner<S> {
    S s;
  }
}
     

无法访问Inner as   部分原始类型(“稀有”类型)

Outer.Inner<Double> x = null; // illegal
Double d = x.s;
     

因为外部本身是原始的,所以也是   所有内在的类,包括   内心,所以不可能   将任何类型的参数传递给它。

     

仅允许使用原始类型   作为对兼容性的让步   遗留代码。使用原始类型   引入后编写的代码   Java编程的通用性   强烈建议不要使用语言。的它   是未来版本的可能   Java编程语言会   不允许使用原始类型。

     

尝试编译时错误   使用a的类型成员   参数化类型作为原始类型。

     

这意味着禁止“罕见”   类型延伸到的情况   限定类型是参数化的,但是   我们尝试使用内部类作为   原始类型:

Outer<Integer>.Inner x = null; // illegal
     

这与我们的情况相反   上面讨论过。没有实际意义   这一半烘焙的理由   类型。在遗留代码中,没有类型   使用参数。在非遗产   代码,我们应该使用泛型类型   正确并传递所有必需的   实际类型参数。

请注意,Inner类具有独立于Outer类的自己的类型参数,并且它仍然被删除。基本上他们不希望我们在同一个实例上混合原始类型和泛型类型,因为它在任何版本中都没有意义(在1.5之前,通用部分将是一个错误,在1.5+原始类型不鼓励,并且甚至可以从将来的版本中删除)

然后还有这个:

  

构造函数的类型(§8.8),   实例方法(§8.8,§9.4),或   非静态场(§8.3)原始的M   类型C不是从它继承的   超类或超级接口是   擦除它在通用中的类型   宣言对应C.   原始类型的静态成员的类型   C与其中的类型相同   对应的通用声明   下进行。

     

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

表示构造函数,实例方法和非静态字段将在原始实例中被视为raw。无论如何,静态成员将被视为通用成员,因为它们不需要加入实例。

答案 1 :(得分:1)

循环的等效表达式是(根据java语言规范):

for (Iterator<String> i = f.getStrings().iterator(); i.hasNext(); ) {
   String a = i.next();
   System.out.println(a);
}

ForEachLoop是一个泛型类,但f使用了原始类型。因此编译器必须假设迭代器可以返回Object个实例或子类型的实例。并发出警告,因为您将原始迭代器分配给参数化迭代器。

只要你的ForEachLoop实例中的集合只包含字符串,你就不会看到运行时错误。

要消除警告,请对实例化部分进行参数化,例如:

ForEachLoop<String> f = new ForEachLoop<String>();

答案 2 :(得分:0)

ForEachLoop<String> f = new ForEachLoop<String>();

答案 3 :(得分:0)

您对非功能版本的期望是什么?您已经声明了一个模板类ForEachLoop<T>,这是一个不完整的类型,直到完全限定。这不会因为

而改变
  • 您的代码位于模板类
  • 您实际上并未使用模板参数。

答案 4 :(得分:0)

Java Generics是使用Type Erasure实现的,这意味着编译器将为泛型类型(例如ForEachLoop<Something>)的每个实例化发出代码,这些实例基本上使用“强制转换为对象”的习惯用法。因此,您的第ForEachLoop f = new ForEachLoop();行将生成一个“直接”实例化,其中type参数默认为object

所以你真正拥有的是ForEachLoop<object>,这就是你得到施法错误的原因。