Java中的通用类型内部类

gen*_*ive 15 java generics netbeans

我已经学习并试验了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) {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和测试代码:

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("");
    }
}
Run Code Online (Sandbox Code Playgroud)

当它运行时,它会给出一个非常神秘的错误消息:

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
Run Code Online (Sandbox Code Playgroud)

现在,通过一些实验,我已经找到了如何防止编译器错误.我可以使Sub类成为静态内部类,或者我需要将Sub类称为Question.Sub <String>.我不能做的是解释为什么我需要这样做.

我已经完成了关于泛型的Java文档的一些阅读,但没有一个涵盖这个特殊情况.

任何人都可以解释为什么代码是当前形式的不兼容类型?

-编辑-

仔细观察,我可以看到我在Netbeans之外得到了同样的行为.如果我有以下结构中的代码:

generics\
generics\Question.java
generics\Answer.java
Run Code Online (Sandbox Code Playgroud)

当我一起编译文件时,我没有收到错误:

C:\>javac generics\Question.java generics\Answer.java

C:\>
Run Code Online (Sandbox Code Playgroud)

但是,当我首先编译问题然后回答,我得到错误:

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
Run Code Online (Sandbox Code Playgroud)

我听过有关类型擦除的提及.在这种情况下是这种情况吗?

AlB*_*lue 1

类型擦除是目前 Java 中泛型实现方式的一个属性。这意味着变量的类型仅在编译时已知,而在运行时未知。因此,例如,在以下内容中:

Map<String,String> map = new HashMap<String,String>();
Run Code Online (Sandbox Code Playgroud)

那么编译器就知道要检查以字符串/字符串为基础放入的项目。但是,编译后的代码对 String,String 一无所知 - 您仍然可以插入错误类型的对象,例如:

Map other = (Map)map;
other.put(new Integer(3), new Double( 4.5 );
Run Code Online (Sandbox Code Playgroud)

问题在于,编译后的代码在传入时不会检查参数类型,运行时也不会检查(因为类型信息已被删除,因此类型被删除)。

我怀疑类型擦除在这里是一个问题 - 因为在编译时,您拥有完整的类型信息 - 但它可能是一个错误。泛型(来自实现)存在相当多的棘手问题,并且 JavaC 和 Eclipse 使用不同的编译器,因此可能会出现不同的错误。在某些情况下,Eclipse 编译器比 Sun 编译器更忠实于规范(因此 Eclipse 会产生错误,而 Sun 不会),这主要是由于类型系统工作方式的复杂性。

所以这很可能是 1.5.0_14 编译器中泛型的一个(或多个)错误......