Почему поиск универсального типа не разрешен во время выполнения в Java?

#java #generics #type-erasure

#java #общие сведения #удаление типа

Вопрос:

Я пытаюсь выяснить, возможно ли получить тип объекта для универсального класса во время выполнения в Java.

Пример:

 public class Member<T> {

    private T id;
    private T complexityLevel;

    // constructor
    public Member(T id, T complexityLevel) {
        this.id = id;
        this.complexityLevel = complexityLevel;
    }

    // getters and setters goes here
}
  

Тестирование..

 public class TestDrive {

    public static void main(String[] args) {
        Member<String> memberString = new Member<String>("1", "100");
        Member<Integer> memberInteger = new Member<Integer>(1, 100);
        Member<Double> memberDouble = new Member<Double>(1.0, 100.00);

        List<Member<?>> members = new ArrayList<>();
        members.add(memberString);
        members.add(memberInteger);
        members.add(memberDouble);

        for (Member<?> aMember : members) {
            if (aMember.getClass().isInstance(String.class))
                System.out.printf("String member is found with id: "   aMember.getId());
            else if (aMember.getClass().isInstance(Integer.class))
                System.out.printf("Integer member is found with id: "   aMember.getId());
            else if (aMember.getClass().isInstance(Double.class))
                System.out.printf("Double member is found with id: "   aMember.getId());
        }
    }
}
  

Возможно ли получить классы-оболочки ( String , Integer , Double и т.д.) для объектов Member класса во время выполнения?

Ответ №1:

Вы не можете определить общие типы во время выполнения из-за стирания типов. Для того, чтобы эта информация была сохранена после удаления, Member<T> необходимо будет взять Class<T> объект в его конструкторе и удерживать его:

 public class Member<T> {

public final Class<T> type;

private T id;
private T complexityLevel;

//constructor   
public Member(T id, T complexityLevel, Class<T> type) {
   id = id;
   this.complexityLevel = complexityLevel;
   this.type = type;
}
  

Затем позже:

 Member<String> mString = new Member<String>("id1", "High", String.class);

...

System.out.println(mString.type.getName());
  

В качестве альтернативы, если id гарантируется, что этого никогда не будет null , вы можете использовать отражение для извлечения Class объекта, представляющего его тип:

 System.out.println(mString.getId().getClass().getName());
  

Конечно, getClass() вернет a Class<? extends T> . Итак, если id на самом деле является подклассом T , вы получите имя этого класса, а не имя T .

Комментарии:

1. @neal_fazio — Вы спрашиваете, как получить просто «строку», «Целое число» и т.д.? Вы можете использовать getSimpleName() вместо getName() .

Ответ №2:

Эта информация (общий тип T) удаляется во время компиляции (стирание типа).

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

Кроме того, String и Integer не являются примитивными типами. «int» — это примитив.