2015-01-12 6 views
4

Нашел факт о неограниченных подстановочных знаках, которые меня раздражают. Например:Вложенные подстановочные знаки

public class Test { 
private static final Map<Integer, Map<Integer, String>> someMap = new HashMap<>(); 

public static void main(String[] args) { 
    getSomeMap(); 
} 

static Map<?, Map<?, ?>> getSomeMap() { 
    return someMap; //compilation fails 
    } 
} 

Это не удается, хотя работает с Map<?, ?> или Map<?, Map<Integer, String>> типа возвращаемого значения.

Может ли кто-нибудь сказать мне точную причину? Заранее спасибо.


Update

Кажется, что я понял, и самое простое объяснение этого вопроса (опуская все эти сложные правила), на мой взгляд, последняя нота в Conversion Capture (link): Capture conversion is not applied recursively.

+0

В чем вопрос? –

+0

'return someMap; // компиляция не завершена' – Dmytro

+2

Возможный дубликат [Несколько подстановочных знаков для общих методов делает Java-компилятор (и меня!) очень путать] (http://stackoverflow.com/questions/3546745/multiple-wildcards-on-a-generic- методы-make-java-compiler-and-me-very-confu) –

ответ

2

Важно понимать значение типа подстановочных знаков.

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

Наоборот, если у вас есть Map<?, Map<?, ?>>, то он имеет неизвестный тип ключа, но тип значения не неизвестен. Это Map<?,?> тип, вспомните информацию выше, которую можно назначить с помощью любой.

Итак, следующий код является законным:

Map<?, Map<?, ?>> map=new HashMap<>(); 
map.put(null, Collections.<String,String>singletonMap("foo", "bar")); 
map.put(null, Collections.<Double,Integer>singletonMap(42.0, 1000)); 
map.put(null, Collections.<Object,Boolean>singletonMap(false, true)); 

Здесь мы прикладываем null ключа, как мы не можем put что-нибудь еще для ключей, но произвольные набранным карт как значения, как это то, что тип значения Map<?, ?> подразумевает: может быть задан с произвольных карт. Обратите внимание, что на iterating over the entries мы можем также set other entries с ключами null на произвольные карты.

Так что я вполне уверен, что вы не хотите, чтобы назначить ваш Map<Integer, Map<Integer, String>> к Map<?, Map<?, ?>> и обнаружить произвольные карты, не будучи Map<Integer, String> как значения впоследствии, и что вы вполне счастливы, что компилятор не позволяет этого.

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

Map<Integer, Map<Integer, String>> someMap = new HashMap<>(); 
Map<?, ? extends Map<?, ?>> map=someMap; 

В системе Map<Integer, String> общего типа является подтипом Map<?, ?>, поэтому вы можете назначить его Map<?, ?>, а также ? extends Map<?, ?>. Это отношение подтипа не отличается от отношения String к Object. Вы можете присвоить любую String переменной типа Object, но если у вас есть Map<?,String>, вы не можете назначить ее Map<?,Object>, но только по Map<?, ? extends Object> по той же причине: карта должна содержать String s как значения, а не принимать произвольные объекты.

Обратите внимание, что вы можете обходить это ограничение.Вы можете сказать:

Map<Integer, Map<Integer, String>> someMap = new HashMap<>(); 
Map<?, Map<?, ?>> map=Collections.unmodifiableMap(someMap); 

Поскольку карта возвращаемый unmodifiableMap не допускает каких-либо изменений, что позволяет расширить ключ и значение типа. Содержащиеся значения имеют заданный тип (т. Е. Map<?, ?>), когда вы запрашиваете карту, но попытки помещать произвольные значения карты, а не отклоняются компилятором, будут отклонены во время выполнения.

+0

Спасибо, но у меня есть один вопрос об этом фрагменте и о подобном выше (о вложенных «Картах»): _ «Вы можете назначить любую« String »переменной типа« Object », но если у вас есть« Map », вы не может назначить его «Map », но только для «Map » по той же причине: карта должна продолжать содержать строки как значения, а не принимать произвольные объекты ». Но в случае' Карта 'мы все равно получаем произвольные объекты. 'get' все еще дает нам' Object', но не 'String'. – Dmytro

+1

Ну, если вы хотите иметь «карту», ​​значения которой «String's» вам нужна «Карта <…,String>». Но вы можете назначить эту карту «Карта <...»? extends Object> ', поскольку одна и та же карта гарантирует, что значения' '' '' '' '' '' '' '' '' '' '' '' '. Но вы не можете * положить * произвольный 'Object' в карту. Вот что? расширяет X' означает, что вы гарантируете, что экземпляры этого типа будут иметь тип 'X' при извлечении, потому что это может быть' X' или подтип 'X', но вы не можете передавать ему экземпляры, t знать, является ли это 'X' или подклассом' X' (и который). – Holger

+0

Возможно [этот документ] (http://www.oracle.com/technetwork/server-storage/ts-6623-159104.pdf) немного помог. – Holger

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