Как сохранить обобщенный тип вложенных обобщенных объектов с токенами классов
Стандартным способом в Java для обхода стирания типов является передача токена класса в конструктор. Например, мы можем определить общий класс свойств следующим образом:
class Prop<T> {
public Prop(Class<T> type) {
this.type = type;
}
Class<T> type;
T t;
}
class IntProp extends Prop<Integer> {
public IntProp() {
super(Integer.class);
}
}
Но что, если я теперь захочу использовать другой аргумент универсального типа, такой как список, а также сохранить его тип универсального типа. Я хотел бы сделать это:
class ListProp<J> extends Prop<ArrayList<J>> {
Class<J> subtype;
public ListProp(Class<J> type) {
super(ArrayList<J>.class);
subtype = type;
}
}
class IntListProp extends ListProp<Integer> {
public IntListProp() {
super(Integer.class);
}
}
Но конечно super(ArrayList<J>.class)
не компилируется и не компилируется super(ArrayList.class)
, Какой лучший способ решить это?
2 ответа
Дженерики кунг-фу, вам нужно сделать ListProp
Класс компиляции это строка:
super((Class<List<T>>)(Class<?>)List.class); // compiles
Попытка кастовать прямо из List.class
в Class<List<T>>
:
super((Class<List<T>>)List.class); //compile error
приводит к ошибке компиляции:
Необратимые типы; не может привести "java.lang.Class" к "java.lang.Class"
Но если вы впервые приведете к типизированному классу Class<?>
, хотя и неизвестного типа, вы можете затем привести его к желаемому типизированному классу.
Полный компилируемый ListProp
класс то.
class ListProp<T> extends Prop<List<T>> {
Class<T> subtype;
public ListProp(Class<T> type) {
super((Class<List<T>>)(Class<?>)List.class); // magic double cast
subtype = type;
}
}
Что-то еще, что вы можете рассмотреть, если вам нужен специальный код для создания / возврата списка, это типизированный геттер для t
на Prop
:
public T getT() {
return t;
}
который вы можете затем ковариантно переопределить в ListProp
вернуть List<T>
@Override
public List<T> getT() {
return Arrays.asList(subtype.newInstance()); // or whatever
}
Следует отметить, что вам нужен токен класса только в том случае, если ваша реализация использует класс, который не показан в вашем примере кода. Если вы на самом деле не используете токен класса, вы можете разрешить вывод типа за вас.
Небольшое вступление
Я знаю, как можно обойти проблему. Стирание типов работает для стирания типов после компиляции, так как они не нужны для запуска. Вероятно, поэтому вы не можете просто получить доступ к списку классов с типом. Список сам по себе использует обобщения, и поэтому не имеет смысла или возможности предоставить класс для этого, поскольку в вашем случае T на самом деле является типом элементов списка. Класс на самом деле ArrayList. Что вы ищете, так это тип коллекции, которую вы используете, и тип элемента, который вы используете. Я немного изменил ваш код, чтобы вы могли принимать конструктор для обоих типов.
- Т теперь тип элементов в коллекции
- J теперь тип коллекции
Код
class ListProp<T, J> extends Prop<T, J> {
Class<T> subtype;
Class<J> subtypeElementList:
public ListProp(Class<T> typeElement, Class<J> typeList) {
super(typeElement, typeList);
subtype = typeElement;
subtypeElementList = typeList;
}
}
class IntListProp extends ListProp<Integer, ArrayList> {
public IntListProp() {
super(Integer.class, ArrayList.class);
}
}
class Prop<T, J> {
// TODO: Maybe here the elements again? Depends on what you want to do...
//
// or maybe just use the integer as you had previously.
public Prop(Class<T> integerClass, Class<J> arrayListClass) {
}
}