给出以下代码:
import java.util.Collections;
import java.util.List;
public class ComeGetSome {
//TODO: private final Some<?> some = new Some();
private final Some some = new Some();
public static void main(String[] args) {
new ComeGetSome().dude();
}
public void dude() {
for (String str : some.getSomeStrings()) { //FIXME: does not compile!
System.out.println(str);
}
}
}
class Some<T> {
public List<String> getSomeStrings() {
return Collections.<String> emptyList();
}
}
它无法编译,因为some.getSomeStrings()
返回原始List
。但方法签名指定它返回List<String>
!
它以某种方式涉及Some
具有类型声明但被引用为原始类型的事实。使用对Some<?>
的引用可以解决问题。但是该方法与类上的类型声明无关!
为什么编译器的行为如此?
答案 0 :(得分:4)
如果使用泛型类的原始(无类型)实例,则将其视为完全 raw,即忽略所有泛型类型信息,即使被忽略的类型为 与已省略的泛型类型相关。
这就是为什么......
private final Some<?> some = new Some();
...修复错误 - 它使用类型版本的Some
(虽然是通配符)
答案 1 :(得分:1)
首先,如果您实际上没有使用已定义的类型参数,则声明类Some
泛型是没有意义的。这实际上是问题的根源。您在此fasion中实例化该类型的成员字段:
private final Some some = new Some();
这是原始类型的声明 - 这是一种泛型类型,其中省略了类型参数。这实际上不仅意味着它自己的类型参数T
被抛弃,而且该类的方法中使用的泛型类型的全部也被忽略,包括<String>
参数in public List<String> getSomeStrings()
。
所以解决方案实际上很简单,引用Joshua Bloch:
不要在新代码中使用原始类型。
为了使这个具体,你建议修复:
private final Some<?> some = new Some();
修复了问题,但实际上会生成编译器警告(未经检查的操作)。 clean 方式可以在语句的右侧声明实际的类型参数,例如:
private final Some<String> some = new Some<String>();
您在泛型参数中输入的类型可以是您想要的任何类型,它取决于您打算使用Some
类的方式。但是如果你实际上不需要type参数,只需删除它,代码就可以正常工作:
class Some { ... }
附注:没有必要在此代码中明确声明String
参数:
return Collections.<String> emptyList();
编译器从方法签名中推断出泛型类型,因此您可以将代码简化为:
public List<String> getSomeStrings() {
return Collections.emptyList();
}
修改强>: 谈到Joshua Bloch,在Google I / O 2011的演讲中,有一个关于这个实际问题的Java益智游戏:http://www.youtube.com/watch?v=wbp-3BJWsU8&t=36m04s