Общие типизированные внутренние классы в Java
Я учился и экспериментировал с 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 <String> . Я не могу объяснить, почему мне нужно это делать.
Я прочитал документацию Java по Generics, но ни один из них не касается этого конкретного случая.
Может ли кто-нибудь объяснить, почему код является несовместимым типом в его текущей форме?
-Edit -
Глядя на это ближе, я вижу, что я получаю такое же поведение за пределами 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
Я слышал что-то, упомянутое о Type Erasure. Так ли это в этой ситуации?
Ответы
Ответ 1
Тип erasure - это свойство способа генерации в Java. Это означает, что тип переменных известен только во время компиляции, но не во время выполнения. Так, например, в следующем:
Map<String,String> map = new HashMap<String,String>();
то компилятор знает, как проверять элементы, помещенные в String/String. Однако скомпилированный код ничего не знает о String, String - вы все равно можете вставлять объекты с неправильным типом, например:
Map other = (Map)map;
other.put(new Integer(3), new Double( 4.5 );
Проблема заключается в том, что скомпилированный код не проверяет типы аргументов при передаче, а также не выполняет runtime (поскольку информация о типе была стерта, следовательно, введите стирание).
Я сомневаюсь, что стирание типа является проблемой здесь, поскольку во время компиляции у вас есть полная информация о типе, но скорее это, вероятно, ошибка. Там довольно много волосатых проблем с дженериками (из реализации), и есть разные компиляторы, которые используются с JavaC и Eclipse, поэтому могут появляться разные ошибки. В некоторых случаях компилятор Eclipse был более точным для спецификации, чем у компилятора Sun (поэтому Eclipse создает ошибки, в то время как Sun не делает этого), и это в основном из-за сложности того, как работает система типов.
Итак, это, скорее всего, одна (или более) ошибка с генериками в компиляторе 1.5.0_14...