Generic без типа в качестве параметра метода - не может разрешить его тип поля

Рассмотрим класс:

public class Foo<T> {
    public List<String> list = new ArrayList<>();
}

что я передаю в качестве параметра метод.


У меня проблема с пониманием, почему здесь тип String не разрешен:

public void test(Foo t) {
    t.list.get(0).contains("test");
}

и t.list рассматривается как List<Object> тогда как здесь все работает нормально:

public void test(Foo<?> t) {
    t.list.get(0).contains("test");
}

и t.list - List<String>.


Другие вопросы о Type Erasure, которые были связаны, подходят к проблеме под другим углом. Не зная ответа на свой вопрос, я не видел связи, и поэтому я не думаю, что этот вопрос является дубликатом.

Ответ 1

При использовании Foo t t - это тип Raw, поэтому его нестатические, не унаследованные элементы, такие как list в приведенном выше коде, также являются необработанными. Здесь соответствующая часть спецификации языка Java (см. Ссылку выше):

Чтобы облегчить взаимодействие с неэквивалентным устаревшим кодом, можно использовать в качестве типа стирание (§4.6) параметризованного типа (§4.5) или стирание типа массива (§10.1), тип элемента которого является параметризованным типом, Такой тип называется сырым типом.

Точнее, необработанный тип определяется как один из:

  • , ,

  • Тип нестатического типа необработанного типа R, который не унаследован от суперкласса или суперинтерфейса R.

и/или

Тип конструктора (§8.8), метод экземпляра (§8.4, §9.4) или нестатическое поле (§8.3) необработанного типа С, которое не унаследовано от его суперклассов или суперинтерфейсов, является необработанным типом, который соответствует стирание его типа в общей декларации, соответствующей C.

Просто объявите list как static и он будет интерпретироваться как List String как ожидалось (для тестирования).

С другой стороны, декларация Foo<?> Не является сырым типом, и, следовательно, list также не считается сырым типом.

Совет позже на этой странице:

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

Примечание: Тип Erasure и сгенерированный байт-код в обоих случаях одинаковы...

Ответ 2

Это как работает стирание типа.

Заменяйте все параметры типа в родовых типах своими границами или объектом, если параметры типа неограничены. Таким образом, полученный байт-код содержит только обычные классы, интерфейсы и методы.

Поскольку ваш метод принимает необработанный тип, компилятор применяет стирание типа, стирая тип String и заменяя его на Object. Вот почему contains не признается, так как Object не имеет такого вызова метода.

Поставляя <?> Вы предоставляете ограниченный тип и который будет использоваться для стирания типа. Там компилятор узнает, contains поскольку String имеет его.