Дженерики Java и стирание типов

Учитывая следующий код:

public void example(Object o) {
  if(o instanceof List<MyType>)
    //do something
}

Я понимаю, что это невозможно (и почему это невозможно), учитывая то, как Java обрабатывает обобщенные типы и стирание типов.

Мой вопрос в том, как лучше всего это сделать? Или единственное, что я могу сделать, это проверить, o - это List<?>?


person kim    schedule 10.02.2010    source источник
comment
Этот вопрос звучит знакомо, как тот, который задавали в течение последних 90 дней или около того. Попробуйте посмотреть [stackoverflow.com/questions/ 1004022 /.   -  person David R Tribble    schedule 10.02.2010


Ответы (5)


Вы не можете проверить больше чем o instanceof List<?>.

Безопасность типов, которую вы получаете от дженериков Java, происходит только во время компиляции. Такой метод, как ваш (который принимает объект, а затем пытается выяснить, что делать), не очень хорошо работает с этим дизайном. Я понимаю, почему когда-нибудь нужен такой динамический метод, но подумайте о том, чтобы дополнить его версиями для типизированных параметров:

public void example(Object o) {
  if(o instanceof List<?>)
     example((List)o);    // can only hope that the type is correct here
}

public void example(List<MyType> list){
    // do something
}

В тех случаях, когда они могут быть использованы, вы получаете все преимущества дженериков. В других случаях вам придется полагаться на людей, читающих вашу Javadoc и передающих только правильные типы.

Чего не может сделать даже вышеприведенный подход, так это иметь два разных пути кода для List<TypeA> и List<TypeB>. Если вам это действительно необходимо, подумайте об использовании ваших собственных типов оболочки ListOfTypeA, ListOfTypeB.

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

 for (Object o: list){
    if (o instanceof TypeA){
    }
    if (o instanceof TypeB){
    }
 }
person Thilo    schedule 10.02.2010

За исключением проверки типов всех элементов в списке, невозможно определить тип универсального параметра в Java, поскольку он существует только во время компиляции и удаляется перед запуском.

Мое предложение было бы, если вам абсолютно не нужно, чтобы метод принимал объект (например, соответствовал спецификации интерфейса или переопределял Object.equals), чтобы принять правильный тип, который вы хотите в качестве параметра для метода, и перегрузить метод с помощью различные другие типы, которые могут вам понадобиться для запуска метода.

person Brandon Bodnar    schedule 10.02.2010

Java стирает информацию о типе для универсальных шаблонов после компиляции, поэтому вы не можете проверить параметр типа таким образом динамически.

Если все пути к этому коду ограничивают параметр типа, выполните следующие действия:

// Return true if object is a list of MyType, false if it is not a list, or is 
// empty
boolean isListOfMyType(Object o) {
    if (o instanceof List) {
        List<?> l = (List<?) o;
        return (l.size() > 0 && (l.get(0) instanceof MyType) 
    }
    return true;
}

является типизированным, хотя он будет работать только в том случае, если список не пуст. Если нет, вам нужно будет изменить приведенное выше, чтобы проверить, все ли элементы проходят тест instanceof (или если они равны нулю, если вы разрешаете нули в своем списке).

Другой вариант - создать подкласс, который расширяет, скажем, ArrayList<MyType>, и использовать его для вашей instanceof проверки.

И последнее, но не менее важное: наличие подкласса, реализующего List<MyType>, позволит вам получить параметр типа с помощью метода Class.getGenericInterfaces(). Подробнее см. this.

Чтобы любой из этих двух последних методов работал, вы должны убедиться, что создание списка всегда создает экземпляр одного из этих типов. Т.е. если вызывающий абонент построит свой собственный ArrayList<MyType>, это не сработает.

person Dean Povey    schedule 10.02.2010

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

Взгляните на: Использование TypeTokens для получения общих параметров

Спасибо

person Richard Gomes    schedule 03.01.2011

Возможно, проверив тип объекта во время выполнения, возможно, что-то вроде:

if (o.getClass() == List.class) ...

Очевидно, вам придется глубже изучить тип класса объекта, чтобы увидеть, точно ли он соответствует List<> и типам элементов списка.

person David R Tribble    schedule 10.02.2010
comment
Во время выполнения класс List ‹ClassA› точно такой же, как List ‹ClassB›. - person Brandon Bodnar; 10.02.2010
comment
Поскольку List - это интерфейс, это, вероятно, плохая идея. - person Adam Batkin; 10.02.2010