Java: несоответствие типов подстановочных знаков приводит к ошибке компиляции
Я создал фабричный класс в своем проекте, который позволил бы мне (теоретически) создавать менеджеров для любого (поддерживаемого) данного типа. Взаимодействие с менеджером позволяет мне изменять определенные свойства данного типа. Проблема, с которой я сталкиваюсь, заключается в том, что когда я пытаюсь создать менеджер для универсального типа, компилятор разрушает мои надежды и мечты.
Следующий код является урезанной версией того, с чем я работаю. Строка, где я пытаюсь создать свой 'test3Manager', не скомпилируется, и я пытаюсь понять, почему это так. Строки под ним показывают "обходной путь", которого я пытаюсь избежать.
import java.util.List;
public class GenTest {
public static void main(String[] args) {
String test1 = "";
IRandomType<String> test2 = null;
IAnotherRandomType<?> test3 = null;
IManager<String> test1Manager = Factory.createManager(test1);
IManager<IRandomType<String>> test2Manager = Factory.createManager(test2);
IManager<IAnotherRandomType<?>> test3Manager = Factory.createManager(test3); // Doesn't compile. Why?
// Work around?
IManager<?> test3ManagerTmp = Factory.createManager(test3);
IManager<IAnotherRandomType<?>> test3Manager2 = (IManager<IAnotherRandomType<?>>) test3ManagerTmp;
}
public interface IRandomType<T> {}
public interface IAnotherRandomType<T> {}
public interface IManager<T> {}
public static class Factory {
public static <T> IManager<T> createManager(T object) {
return null;
}
}
}
Точное сообщение об ошибке компиляции:
Type mismatch: cannot convert from GenTest.IManager<GenTest.IAnotherRandomType<capture#1-of ?>> to GenTest.IManager<GenTest.IAnotherRandomType<?>>
Подобные вопросы задавались ранее (см. Ниже); однако я не знаю, считается ли этот вопрос дубликатом их. Я утверждаю это только потому, что у меня возникают проблемы с выводом ответов на эти вопросы на мой. Я надеюсь, что кто-то сможет уточнить, что я делаю неправильно с использованием дженериков.
Связанные вопросы по SO:
1 ответ
Используйте следующее:
IManager<IAnotherRandomType<?>> test3Manager =
Factory.<IAnotherRandomType<?>>createManager(test3);
Это всего лишь случай, когда вывод типа компилятора падает на его лицо, поэтому необходимо явно предоставить аргумент типа для T
,
Более технически:
test3
объявлено, чтобы иметь тип IAnotherRandomType<?>
, где ?
является подстановочным знаком - своего рода параметр одноразового типа, представляющий некоторый определенный неизвестный тип. Это то, на что ссылается компилятор, когда говорит capture#1-of ?
, Когда вы проходите test3
в createManager
, T
выводится как IAnotherRandomType<capture#1-of ?>
,
В то же время, test3Manager
объявлено, чтобы иметь тип IManager<IAnotherRandomType<?>>
, который имеет вложенный подстановочный знак - он не ведет себя как параметр типа, а представляет какой-либо тип.
Так как генерики не являются ковариантными, компилятор не может конвертировать из IManager<IAnotherRandomType<capture#1-of ?>>
в IManager<IAnotherRandomType<?>>
,
Больше чтения на вложенных подстановочных знаках: