Как сохранить обобщенный тип вложенных обобщенных объектов с токенами классов

Стандартным способом в 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. Что вы ищете, так это тип коллекции, которую вы используете, и тип элемента, который вы используете. Я немного изменил ваш код, чтобы вы могли принимать конструктор для обоих типов.


  1. Т теперь тип элементов в коллекции
  2. 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) {

            }
        }
Другие вопросы по тегам