我一直在学习和试验Java Generics一段时间,但我遇到了一些我无法解释的问题。以下面的代码为例:
public class Question {
public <T> Sub<T> getSub(Class<T> c) {
return new Sub<T>(c);
}
public class Sub<S> {
private Class<S> c;
public Sub(Class<S> c) {
this.c = c;
}
public void add(S s) {
}
}
}
测试代码:
import generics.Question.Sub;
public class Answer {
public static void main(String [] args) {
Question q = new Question();
Sub<String> s = q.getSub(String.class);
s.add("");
}
}
当它运行时,它会给出一个非常神秘的错误消息:
C:\Answer.java:8: incompatible types
found : generics.Question.Sub<java.lang.String>
required: generics.Question.Sub<java.lang.String>
Sub<String> s = q.getSub(String.class);
1 error
现在,通过一些实验,我已经找到了如何防止编译器错误。我可以使Sub类成为静态内部类,或者我需要将Sub类称为Question.Sub&lt; String&gt;。我不能做的是解释为什么我需要这样做。
我已经完成了关于泛型的Java文档的一些阅读,但没有涉及这个特殊情况。
有人可以解释为什么代码是当前形式的不兼容类型吗?
- 编辑 -
仔细观察,我可以看到我在Netbeans之外得到了同样的行为。如果我有以下结构中的代码:
generics\
generics\Question.java
generics\Answer.java
当我一起编译文件时,我没有收到错误:
C:\>javac generics\Question.java generics\Answer.java
C:\>
然而,当我首先编译问题然后回答,我得到错误:
C:\>javac generics\Question.java
C:\>javac generics\Answer.java
generics\Answer.java:8: incompatible types
found : generics.Question.Sub<java.lang.String>
required: generics.Question.Sub<java.lang.String>
Sub<String> s = q.getSub(String.class);
^
1 error
我听过有关类型擦除的提及。在这种情况下是这种情况吗?
答案 0 :(得分:1)
类型擦除是当前在Java中实现泛型的方式的属性。这意味着变量的类型只在编译时知道,而不是在运行时。因此,例如,在以下内容中:
Map<String,String> map = new HashMap<String,String>();
然后编译器知道检查以字符串/字符串为基础的项目。但是,编译后的代码对String,String一无所知 - 您仍然可以使用错误的类型插入对象,例如:
Map other = (Map)map;
other.put(new Integer(3), new Double( 4.5 );
问题是编译后的代码在传入时不会检查参数类型,也不会检查运行时(因为类型信息已被删除,因此,类型擦除)。
我怀疑类型擦除是一个问题 - 因为在编译时,你有完整的类型信息 - 但它可能是一个错误。泛型(从实现中)有很多毛茸茸的问题,并且有不同的编译器在使用JavaC和Eclipse,因此可能会出现不同的错误。在某些情况下,Eclipse编译器比Sun编译器更忠实于规范(因此Eclipse会创建错误,而Sun则不会),而这主要是由于类型系统工作方式的复杂性。
所以很可能在1.5.0_14编译器中有一个(或多个)泛型错误......