2012-02-14 4 views
1

У меня есть следующий код:Почему следующий листинг приводит к ошибке компиляции?

public static<F, S> void xyz() { 
    class Pair<X, Y> {}  
    class Role<F> {} 

    Map<?, List<Pair<?, ?>>> map = null; 
    Role<F> role = null; 

    List<Pair<F, S>> result = (List<Pair<F, S>>) map.get(role); 
} 

К сожалению, Java жалуется бросок на последнюю строку. Почему это происходит?

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

+1

Каков ожидаемый тип возврата? – Vladimir

+0

Вы также должны указать подпись метода - и, возможно, подпись класса (где вы указали F и S?). – home

+0

Я добавил более полный пример. –

ответ

-1

«Приведение должно допускаться во всех местах, где есть возможность правильного соответствия типа». В этом случае нет возможности правильного соответствия типа.

Объект никогда не может быть экземпляром как List<A>, так и List<B>, если A и B являются конкретными и не то же самое, даже если A является подтипом B или любым другим. Например, у вас не может быть объекта, который равен List<String> и List<Object>. Существует (концептуально) определенный параметр типа для каждого объекта. Сначала вы должны понять эту часть.

Ваш пример такой же - List<Pair<F, S>> и List<Pair<?, ?>>. По той же причине, что и для String и Object выше, объект, который имеет тип List<Pair<?, ?>>, также не может быть List<Pair<F, S>>.

Обновление: больше пояснений в случае, если это поможет. @Konstantin: Если вы пишете Pair<?, ?>, то эти параметры типа на верхнем уровне, которые являются ? (подстановочные знаки), являются гибкими. Pair<T1, T2> совместим с Pair<?, ?>. Однако в List<Pair<?, ?>>? не находится на верхнем уровне. Параметр типа на верхнем уровне - Pair<?, ?>, который не является подстановочным знаком и, следовательно, не является гибким. Не имеет значения, что у вас будет ? глубже.Если у вас есть List<? extends Pair<?, ?>>, то верхний уровень является подстановочным знаком и является гибким.

Что вы, возможно, хотите, это List<? extends Pair<?, ?>>. Возможно, это поможет вам рассмотреть разницу между List<Pair<?, ?>> и List<? extends Pair<?, ?>>. List<Pair<?, ?>> говорит, что это список, и этот параметр типа точно тип Pair<?, ?> (и ничего больше, а не Pair<T1, T2> и т. Д.). List<? extends Pair<?, ?>> говорит, что это список, а параметр типа - это подтип Pair<?, ?> (и включает в себя Pair<T1, T2>).

+0

Собственно map.get (role) компилирует. Вот мои рассуждения, почему код верен. ? extends - это экзистенциальный тип. Это означает, что существуют T1 и T2, что для этого экземпляра List > его фактическим типом является List >. Итак, почему я могу указать типы T1 и T2, если у меня есть информация? –

+0

Я переформулировал свой вопрос: http://stackoverflow.com/questions/9274547/how-to-create-a-list-with-complex-structure-inside –

+0

@Konstantin: about map.get(): oh right, nevermind , – newacct

0

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

List<Pair<?, ?>> ==>> List<Pair<F, S>> 

Вы не можете решить эту проблему folowing образом:

List<Pair<?, ?>> result = (List<Pair<?, ?>>) map.get(role); 
+0

На самом деле я знаю, как обойти эту проблему. Я могу изменить последнюю строку на (Список >) (Список) map.get (role); и все будет хорошо. Я хочу понять, почему следующий код неверен с точки зрения разработчика языка. –

+0

Что это значит: public static void xyz()? потому что метод возвращаемого типа недействителен, так что среднее значение? Я не понимаю. Пожалуйста, изучите это. – MJM

+0

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

1

Актерский не имеет никакого смысла. Это не наоборот.

Аналогичные можно литой, где мы имеем подстановочный аргумент:

List<String> ls; 
List<?> cast = ls; 

В этом случае я не могу впоследствии добавьте Integer к объекту ls/cast.

cast.add(Integer.valueOf(0)); // Not going to work. 

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

List<List<String>> strses; 
List<List<?>> cast = strses; // Suppose this actually worked. 
List<Integer> ints; 
List<?> wildInts = ints; 
cast.add(0, wildInts); // Good. 
List<String> wasInts = strses.get(0); // Oops!! 
+0

Примеры не содержат выражения. Я понимаю, почему эти примеры не работают, см. Другой вопрос здесь: http://stackoverflow.com/questions/9152302/why-is-the-following-java-code-leads-to-compilation-error Однако, мы можем явно передать String из Object, и это может иметь некоторый смысл в некоторой ситуации.IMO, то же самое с литой из списка > в список >. Мы могли бы теперь из некоторых других частей программы, что этот список на самом деле является пар

+0

Я переформулировал свой вопрос: http://stackoverflow.com/questions/9274547/how-to-create-a-list-with-complex- structure-inside –

1

Когда объект создан, он имеет точный тип. Этот тип может быть ArrayList<Pair<F, S>> который конечно подтип List<Pair<F, S>>. Это, однако, как указал Том, не является подтипом List<Pair<?,?>>. Таким образом, единственный способ, которым ваш актер был бы прав, был, если бы был некорректный бросок в другом месте. Если в какой-то момент генерируются дженерики, то ваш бросок или этот необоснованный бросок сломаются. Это объяснение того, почему этот отбор отклонен.

1

Дженерики не являются совершенно новой системой. Внутренне все это основано на приведениях (из-за совместимости с байтовым кодом (назад)).

Расширение ответа Тома, давайте делать простейшие возможные примеры:

List<? extends Object> genericList; // List<?> is short for List<? extends Object> 
List<String> concreteList = new LinkedList<String>(); 

genericList = concreteList; 
concreteList = (List<String>) genericList; //(caution: uncheck cast) 

Таким образом, это работает с Струнным расширяет объект. Давайте перейдем дальше и сделаем списки списков:

List<List<?>> listOfGenericLists; 
List<List<String>> listOfStrings = new LinkedList<List<String>>(); 
//does not work "only because" String extends Object: 
listOfGenericLists = listOfStrings; //compile error 

Это ту же ошибку, что и в вашем примере. Интуиция заключается в том, что это должно работать, потому что String расширяет Object, и поэтому List<String> должен расширять List<Object>. Ну, это не так. Вы, наверное, не удивились бы этому: List> listOfIntegers; listOfIntegers = listOfStrings; // также компилируем ошибку

Давайте посмотрим, что произойдет, когда мы сделаем интуицию явной. Давайте фактически продолжим из списков:

List<? extends List<?>> genericListOfLists = new LinkedList<List<?>>(); 
List<? extends List<String>> genericListOfStringLists = new LinkedList<List<String>>(); 
genericListOfLists = listOfStrings; 
genericListOfStringLists = listOfStrings; 
listOfStrings = (List<List<String>>) genericListOfLists; //(caution: unchecked cast) 
listOfStrings = (List<List<String>>) genericListOfStringLists; // works as before (caution: uncheck cast) 

Ah-ha! Теперь интуиция вернулась на трассу. Правильно? не На самом деле нет:

List<? extends List<Integer>> listOfIntegerLists; 
listOfIntegerLists = (List<List<Integer>>) genericListOfLists; //(caution: uncheck cast) 
listOfIntegerLists = (List<List<Integer>>) genericListOfStringLists; //OUCH (no compilation error just an unchecked cast!) 

Причина в том, - как было сказано ранее: От типа точки зрения все дженерики одни и те же и литейные работы на уровне типа.

Я могу порекомендовать Java Generics and Collections от Мориса Нафталина и Филиппа Вадлера (O'Reilly).

+0

Я переформулировал свой вопрос: http://stackoverflow.com/questions/9274547/how-to-create-a-list-with-complex-structure-inside –

Смежные вопросы