Тип безопасности: непроверенный актерский состав - PullRequest
229 голосов
/ 04 ноября 2008

В моем весеннем контексте приложения у меня есть что-то вроде:

<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
    <entry key="some_key" value="some value" />
    <entry key="some_key_2" value="some value" />   
</util:map>

В Java-классе реализация выглядит следующим образом:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

В Eclipse я вижу предупреждение:

Безопасность типов: непроверенное приведение от объекта к HashMap

Что я сделал не так? Как мне решить проблему?

Ответы [ 9 ]

280 голосов
/ 04 ноября 2008

Проблема в том, что приведение является проверкой во время выполнения, но из-за стирания типа во время выполнения фактически нет разницы между HashMap<String,String> и HashMap<Foo,Bar> для любых других Foo и Bar.

Используйте @SuppressWarnings("unchecked") и держите нос. Да, и кампания за усовершенствованные дженерики в Java:)

234 голосов
/ 04 ноября 2008

Ну, во-первых, вы тратите память с новым вызовом создания HashMap. Ваша вторая строка полностью игнорирует ссылку на этот созданный hashmap, делая его доступным для сборщика мусора. Так что, не делайте этого, используйте:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

Во-вторых, компилятор жалуется, что вы приводите объект к HashMap, не проверяя, является ли он HashMap. Но, даже если вы должны были сделать:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

Вы, вероятно, все еще получили бы это предупреждение. Проблема в том, что getBean возвращает Object, поэтому неизвестно, что это за тип. Непосредственное преобразование его в HashMap не вызовет проблемы во втором случае (и, возможно, в первом случае не будет предупреждения, я не уверен, насколько педантичен компилятор Java с предупреждениями для Java 5). Однако вы конвертируете его в HashMap<String, String>.

HashMaps - это действительно карты, которые принимают объект в качестве ключа и имеют объект в качестве значения, HashMap<Object, Object>, если хотите. Таким образом, нет гарантии, что когда вы получите ваш bean-компонент, он может быть представлен как HashMap<String, String>, потому что у вас может быть HashMap<Date, Calendar>, потому что возвращаемое неуниверсальное представление может иметь любые объекты.

Если код компилируется, и вы можете выполнить String value = map.get("thisString"); без каких-либо ошибок, не беспокойтесь об этом предупреждении. Но если карта не полностью содержит строковые ключи для строковых значений, вы получите ClassCastException во время выполнения, потому что в этом случае универсальные блоки не могут этого предотвратить.

73 голосов
/ 15 ноября 2012

Как показывают приведенные выше сообщения, список нельзя различить между List<Object> и List<String> или List<Integer>.

Я решил это сообщение об ошибке для аналогичной проблемы:

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

со следующим:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

Объяснение: Первое преобразование типов проверяет, что объект является списком, не заботясь о типах, содержащихся в нем (поскольку мы не можем проверить внутренние типы на уровне списка). Второе преобразование теперь требуется, потому что компилятор знает только, что List содержит какие-то объекты. Это проверяет тип каждого объекта в Списке при доступе к нему.

26 голосов
/ 04 ноября 2008

Предупреждение только это. Предупреждение. Иногда предупреждения не имеют значения, иногда нет. Они привыкли обращать ваше внимание на то, что компилятор считает проблемой, но может и не быть.

В случае приведения это всегда дает предупреждение в этом случае. Если вы абсолютно уверены, что конкретное приведение будет безопасным, то вам следует рассмотреть возможность добавления аннотации, подобной этой (я не уверен в синтаксисе) перед строкой:

@SuppressWarnings (value="unchecked")
9 голосов
/ 04 ноября 2008

Вы получаете это сообщение, потому что getBean возвращает ссылку на объект, и вы приводите его к правильному типу. Java 1.5 дает вам предупреждение. Такова природа использования Java 1.5 или выше с кодом, который работает следующим образом. Spring имеет типобезопасную версию

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

в списке задач.

5 голосов
/ 13 апреля 2016

Если вы действительно хотите избавиться от предупреждений, вы можете создать класс, расширяющийся от универсального класса.

Например, если вы пытаетесь использовать

private Map<String, String> someMap = new HashMap<String, String>();

Вы можете создать новый класс, такой как

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

Тогда, когда вы используете

someMap = (StringMap) getApplicationContext().getBean("someMap");

Компилятор знает, что такое (больше не универсальные) типы, и предупреждений не будет. Это не всегда может быть идеальным решением, некоторые могут утверждать, что этот тип отрицает цель универсальных классов, но вы все еще повторно используете весь тот же код из универсального класса, вы просто объявляете во время компиляции, какой Вы хотите использовать.

1 голос
/ 28 августа 2018

Решение, чтобы избежать непроверенного предупреждения:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
1 голос
/ 16 декабря 2016

Ниже приведен код причины Тип безопасности Предупреждение

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

Обход

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

Шаг 1: Создание новой временной карты

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

Шаг 2: Создание главной карты

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

Шаг 3: Итерация временной карты и установка значений в основную карту

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }
1 голос
/ 20 октября 2016

Другое решение, если вы часто используете один и тот же объект и не хотите засорять свой код @SupressWarnings("unchecked"), - это создать метод с аннотацией. Таким образом, вы централизуете приведение и надеетесь уменьшить вероятность ошибки.

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}
...