Почему вы можете разыгрывать Карты разных типов? - PullRequest
2 голосов
/ 07 августа 2011

Сегодня я столкнулся со странностью, которую я не совсем понимаю.Возьмите этот код, например:

Object iMap = new HashMap<Integer, Object>() {{
    put(5, "thing1");
    put(6, "thing2");
}};
Map<String, Object> sMap = (Map<String, Object>)iMap;

// No error, prints out java.lang.Integer:
System.out.println(new ArrayList(sMap.keySet()).get(0).getClass().getName();

// No error, prints out 5:
Object key = new ArrayList<String>(sMap.keySet()).get(0);
System.out.println(key.toString());

// ClassCastException:
String s = new ArrayList<String>(sMap.keySet()).get(0);

Итак, что дает?Почему я могу без проблем привести карту с ключами типа Integer к одному из типов String?Разве это не должно вызвать ошибку?И почему я могу даже привести к ArrayList<String> и все еще без ошибок?Предположительно, это список только строк, но я могу извлечь из него целое число.

Я немного озадачен этим, и мне интересно, знает ли кто-нибудь здесь достаточно о внутренней работе этих классов, чтобыпомоги мне.

Ответы [ 2 ]

3 голосов
/ 07 августа 2011

Вы можете разыграть Map<Integer, Object> на Map<String, Object> "без проблем" ... пока не попытаетесь использовать it.

Проблема начинается с этой строки:

Map<String, Object> sMap = (Map<String, Object>)iMap;

, где компилятор предупреждает вас этим сообщением:

Тип безопасности: непроверенное приведение с объекта на карту

Вы проигнорировали это предупреждение.

Все это происходит из-за стирания типа во время выполнения - во время выполнения нет типов, например, у вас есть только Map и т. Д. Типы просто есть при компиляции, чтобы помочь не делай то, что ты здесь делаешь.

Причина взрыва этой строки:

String s = new ArrayList<String>(sMap.keySet()).get(0);

заключается в том, что sMap фактически ссылается на карту, в которой в ее записях указаны целые числа для ключей. Когда вы на самом деле пошли, чтобы вытащить один из ключей, это было Integer, которое Java затем пытается присвоить String ... boom!

кстати, эта часть не компилируется:

Object iMap = new HashMap<Integer, Object>();
iMap.put(5, "thing1");
iMap.put(6, "thing2");

вам нужно привести iMap к Map<Integer, Object> следующим образом:

Object iMap = new HashMap<Integer, Object>();
((Map<Integer, Object>)iMap).put(5, "thing1");
((Map<Integer, Object>)iMap).put(6, "thing2");
0 голосов
/ 07 августа 2011

Первые четыре строки вашего кода не будут компилироваться, поскольку iMap должен быть объявлен как минимум как Map для вызова таких методов, как get (...). Но ваша другая проблема иллюстрирует, почему важно использовать обобщения при объявлении переменных. Так что если вы объявили iMap таким образом:

  Map<Integer, Object> iMap = new HashMap<Integer, Object>();

Компилятор будет правильно жаловаться, когда вы попытаетесь привести его здесь:

Map<String, Object> sMap = (Map<String, Object>)iMap;
...