Универсальные типизированные внутренние классы в Java - PullRequest
15 голосов
/ 03 марта 2010

Я некоторое время изучал и экспериментировал с 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 . Что я не могу сделать, так это объяснить, почему мне нужно это сделать.

Я немного прочел документацию по 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

Я слышал что-то упомянутое о Типе Erasure. Так ли это в этой ситуации?

1 Ответ

1 голос
/ 03 марта 2010

Стирание типа является свойством того, как дженерики в настоящее время реализуются в Java. Это означает, что тип переменных известен только во время компиляции, но не во время выполнения. Так, например, в следующем:

Map<String,String> map = new HashMap<String,String>();

тогда компилятор знает, что нужно проверять элементы, помещаемые на основе String / String. Однако скомпилированный код ничего не знает о String, String - вы все равно можете вставлять объекты с неверным типом, например ::1004*

Map other = (Map)map;
other.put(new Integer(3), new Double( 4.5 );

Проблема в том, что скомпилированный код не проверяет типы аргументов при передаче и время выполнения (поскольку информация о типе была удалена, следовательно, стирание типа).

Я сомневаюсь, что стирание типов является проблемой здесь - поскольку во время компиляции у вас есть полная информация о типах - но скорее это ошибка. Существует довольно много проблем с генериками (из реализации), и в JavaC и Eclipse используются разные компиляторы, поэтому они могут показывать разные ошибки. В некоторых случаях компилятор Eclipse был более точным в спецификации, чем компилятор Sun (поэтому Eclipse создает ошибки, а Sun нет), и это в основном связано со сложностью работы системы типов.

Так что, скорее всего, это одна (или более) ошибка с обобщениями в компиляторе 1.5.0_14 ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...