Вопрос по java, generic-method, generics, raw-types – Объединение необработанных типов и общих методов

24

Вот вопрос, этот первый список кода компилируется просто отлично (JDK 1.6 | JDK 1.7):

ArrayList<String> a = new ArrayList<String>();
String[] s = a.toArray(new String[0]);

Однако, если я объявлюList ссылка как необработанный тип:

ArrayList a = new ArrayList();
String[] s = a.toArray(new String[0]);

Я получаю сообщение об ошибке компилятораString[] требуется, ноObject[] был найден.

Это означает, что мой компилятор интерпретирует универсальный метод как возвращающийObject[] несмотря на получениеString[] в качестве аргумента.

Я дважды проверилtoArray(myArray) подпись метода:

<T> T[] toArray(T[] a);

Поэтому это параметризованный метод, чей параметр типа<T> не имеет никакого отношения к списку (т.е.<E>).

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

Does anyone has any idea why this code does not compile? Does anybody knows any reference where this behavior is documented?
@HovercraftFullOfEels Спасибо за предложение. Я знаю, что могу разыграть его, но я учусь на сертификацию программиста, и я только что узнал об этом, играя с некоторым универсальным кодом, и не могу объяснить это. Никогда раньше не сталкивался с чем-то подобным. Итак, я больше ищу объяснение того, почему это происходит. Edwin Dalorzo
Возможный дубликат:stackoverflow.com/questions/1073786. creemama
Также интересно, если вы параметризуете с подстановочным знаком,ArrayList<?> a = new ArrayList();ошибка уходит. Кроме того, ошибка исчезнет, еслиInteger используется как тип:ArrayList<Integer> a = new ArrayList();. creemama
Что делать, если вы используете(String[])a.toArray(); Поскольку вы не используете дженерики, кастинг является обязательным. Hovercraft Full Of Eels
Интересная находка. Как ни странно, если вы явно укажете универсальный параметр как Object (ArrayList & lt; Object & gt;), ошибка исчезнет. Что странно, так как объект должен быть по умолчанию для пропущенных типов. Perception

Ваш Ответ

5   ответов
1

что это поведение может быть «решено». Используйте два интерфейса: базовый неуниверсальный интерфейс и универсальный интерфейс. Тогда компилятор знает, что все функции базового неуниверсального интерфейса не являются универсальными, и будет обрабатывать их следующим образом.

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

Soution via interface inheritence:

public interface GenericInterface<X extends Y> extends BaseInterface
{
    X getValue();
}

public interface BaseInterface
{
    String getStringValue();
}

Теперь вы можете делать следующее без предупреждения и проблем:

GenericInterface object = ...;
String stringValue = object.getStringValue();
0

переданного в метод toArray (), поскольку ваш ArrayList является непараметрическим списком, он знает только, что он содержит объекты, то есть его.a.toArray() всегда будет возвращать массив Object []. Опять же, вы должны бросить его(String[]) (со всеми вытекающими отсюда опасностями), если вы хотите указать, что он содержит определенный тип String.

Я ценю ответ, но внутренний массивArrayList  всегда типаObject[] независимо от того, использую я дженерики или нет. Это не влияет на параметризованный метод, когда я использую параметризованныйArrayList  ссылка. Тем не менее, рассматриваемый метод параметризован, ожидаяT[]  как его тип возврата. Почему он игнорируется компилятором, если он не имеет ничего общего с параметризованным типомArrayList? Я до сих пор не вижу связи междуT а такжеE такой, что компилятор ведет себя так. Edwin Dalorzo
8

компилятор обрабатывает его как необработанный тип, и, следовательно, каждый универсальный тип становитсяObject и поэтому вы не можете пройтиString[] потому что это нужноObject[]
Так вот, сделка - Если вы используете

List l = new ArrayList<String>();

Ты используешьraw type и все его элементы экземпляра заменяются его аналогами стирания. Особенноeach параметризованный тип, появляющийся в объявлении метода экземпляра, заменяется его необработанным аналогом. См. JLS 4.8 для деталей.

+1 Очень интересно.
Компилятор знает, объявили ли вы «a»; быть ArrayList или ArrayList & lt; String & gt ;. Это знает это ... ну, глядя на то, что вы написали ... ;-)
Если подумать, я считаю, что говорить, что компилятор переходит в режим компиляции 1.4, довольно неточно. Я понимаю, что вы имели в виду, что он обрабатывает ссылку, как если бы она была необработанным типом (как в JDK 1.4, когда не было обобщений), но ваше утверждение можно интерпретировать так, как если бы вы имели в виду, что код скомпилирован, как если бы я передалtarget=1.4 моему компилятору, так как он генерирует 1.4-совместимые байт-коды, что я не считаю нужным. В любом случае, вы можете уточнить, что прежде чем кто-то другой заставит вас заплатить цену с любыми отрицательными голосами ;-) Edwin Dalorzo
+1 за ссылку JLS. Edwin Dalorzo
Как компилятор может сказать, что я не использую обобщенные значения, параметр типа List не имеет ничего общего с параметром типа обобщенного метода toArray? Edwin Dalorzo
6

которое я нашел в спецификации, чтобы описать это наблюдаемое поведение:

The type of a constructor (§8.8), instance method (§8.8, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.

It is a compile-time error to pass actual type parameters to a non-static type member of a raw type that is not inherited from its superclasses or superinterfaces.

Исходя из вышеизложенного и наблюдаемого поведения, я думаю, можно с уверенностью сказать, чтоall универсальные типы параметров удаляются из необработанного типа. Конечно, использование необработанных типов не рекомендуется в устаревшем коде:

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

+1 для жирной ссылки. Я ценю исследование восприятия. Это определенно проясняет ситуацию для меня. Edwin Dalorzo
29

что вы ожидаете, но если вы ссылаетесь на родовой класс в необработанном виде, вы теряете возможность использовать дженерики.in any way например, члены. Это также не ограничено общими методами, проверьте это:

 public class MyContainer<T> {

     public List<String> strings() {
         return Arrays.asList("a", "b");
     }
 }

 MyContainer container = new MyContainer<Integer>();
 List<String> strings = container.strings(); //gives unchecked warning!

Это соответствующая часть JLS (4.8):

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

@MarkPeters Удивительно, это то, что я искал. б (^_^) д Edwin Dalorzo
@ Эдвин Далорсо - см. Мой отредактированный ответ - JLS 4.8
+1 Похоже, что компилятор намекает на использование необработанного типа и отключает любые родовые типы оттуда в данной ссылке. Хм, интересно. Интересно, задокументировано ли это в JLS. Вы случайно не знаете? Edwin Dalorzo
@ Эдвин: Я процитировал это в своем ответе, похоже, что Премрай тоже его нашел.

Похожие вопросы