Несоответствие типов для обобщенных классов - PullRequest
20 голосов
/ 02 октября 2008

У меня есть следующий код, который не будет компилироваться, и хотя есть способ сделать его компиляцией, я хочу понять, почему он не компилируется. Может кто-нибудь объяснить мне, почему я получаю сообщение об ошибке, которое я опубликую в конце, пожалуйста?

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
        Class<T> testType = type == null ? Test.class : type; //Error here
        System.out.println(testType);
    }
}

Type mismatch: cannot convert from Class<capture#1-of ? extends Test> to Class<T>

При приведении Test.class к Class<T> это компилируется с предупреждением Unchecked cast и работает отлично.

Ответы [ 3 ]

23 голосов
/ 02 октября 2008

Причина в том, что Test.class имеет тип Class . Вы не можете присвоить ссылку типа Class переменной типа Class , так как они не одно и то же. Это, однако, работает:

Class<? extends Test> testType = type == null ? Test.class : type;

Подстановочный знак позволяет назначать ссылки на Class и Class на testType.

Тонну информации о поведении обобщенных Java-программ можно найти по адресу Angelika Langer FAQ по обобщенным Java-компонентам . Я приведу пример, основанный на некоторой информации, которая использует базовый API Java класса Number heirarchy.

Рассмотрим следующий метод:

public <T extends Number> void testNumber(final Class<T> type)

Это позволяет успешно компилировать следующие операторы:

testNumber(Integer.class);
testNumber(Number.class);

Но следующее не скомпилируется:

testNumber(String.class);

Теперь рассмотрим следующие утверждения:

Class<Number> numberClass = Number.class;
Class<Integer> integerClass = numberClass;

Вторая строка не компилируется и выдает эту ошибку Type mismatch: cannot convert from Class<Number> to Class<Integer>. Но Integer расширяет Number, так почему же он терпит неудачу? Посмотрите на следующие два утверждения, чтобы понять почему:

Number anumber = new Long(0);
Integer another = anumber;

Довольно легко понять, почему 2-я строка здесь не компилируется. Вы не можете присвоить экземпляр Number переменной типа Integer, потому что нет способа гарантировать, что экземпляр Number имеет совместимый тип. В этом примере Number - это на самом деле Long, который, безусловно, не может быть присвоен Integer. На самом деле ошибка также является несовпадением типов: Type mismatch: cannot convert from Number to Integer.

Правило состоит в том, что экземпляр не может быть назначен переменной, которая является подклассом типа экземпляра, поскольку нет гарантии, что он совместим.

Дженерики ведут себя аналогичным образом. В сигнатуре обобщенного метода T - это просто заполнитель, указывающий, что метод позволяет компилятору. Когда компилятор встречает testNumber(Integer.class), он по существу заменяет T на Integer.

Подстановочные знаки добавляют дополнительную гибкость, так как компилируется следующее:

Class<? extends Number> wildcard = numberClass;

Поскольку Class<? extends Number> обозначает любой тип, который является Number или подклассом Number, это совершенно законно и потенциально полезно во многих обстоятельствах.

4 голосов
/ 03 октября 2008

Предположим, я продлил тест:

public class SubTest extends Test {
  public static void main(String args[]) {
    Test t = new Test();
    t.testT(new SubTest());
  }
}

Теперь, когда я вызвал testT, параметр типа <T> равен SubTest, что означает, что переменная testType равна Class<SubTest>. Test.class имеет тип Class<Test>, который нельзя назначить переменной типа Class<SubTest>.

Объявление переменной testType как Class<? extends Test> является правильным решением; приведение к Class<T> скрывает настоящую проблему.

1 голос
/ 02 октября 2008

Удалите условное и ошибка немного приятнее ...

public class Test {
    public static void main(String args[]) {
        Test t = new Test();
        t.testT(null);
    }

    public <T extends Test> void testT(Class<T> type) {
    Class<T> testClass = Test.class;
        System.out.println(testClass);
    }
}


Test.java:10: incompatible types
found   : java.lang.Class<Test>
required: java.lang.Class<T>
        Class<T> testClass = Test.class;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...