Как я обращаюсь к непроверенным предупреждениям броска? - PullRequest
551 голосов
/ 04 февраля 2009

Затмение дает мне предупреждение следующего вида:

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

Это вызов API, который я не могу контролировать, который возвращает Object:

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
  HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
  return theHash;
}

Я бы хотел, по возможности, избегать предупреждений Eclipse, поскольку теоретически они указывают, по крайней мере, на потенциальную проблему с кодом. Я пока не нашел хорошего способа устранить этот. Я могу извлечь отдельную строку из метода и добавить к этому методу @SuppressWarnings("unchecked"), тем самым ограничивая влияние блока кода, где я игнорирую предупреждения. Есть лучшие варианты? Я не хочу отключать эти предупреждения в Eclipse.

До того, как я пришел к коду, он был проще, но все равно вызывал предупреждения:

HashMap getItems(javax.servlet.http.HttpSession session) {
  HashMap theHash = (HashMap)session.getAttribute("attributeKey");
  return theHash;
}

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

HashMap items = getItems(session);
items.put("this", "that");

Type safety: The method put(Object, Object) belongs to the raw type HashMap.  References to generic type HashMap<K,V> should be parameterized.

Ответы [ 24 ]

7 голосов
/ 11 августа 2011

Вспомогательная функция Objects.Unchecked в ответе Эско Луонтолы (Esko Luontola) выше - отличный способ избежать беспорядка в программе.

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

@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;

Использование утилиты намного чище, и все еще очевидно, что вы делаете:

this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());

Примечание: Я чувствую, что важно добавить, что иногда предупреждение действительно означает, что вы делаете что-то не так, как:

ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList; 

 // this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList  = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here

Компилятор говорит вам, что это приведение НЕ будет проверяться во время выполнения, поэтому ошибка времени выполнения не возникнет, пока вы не попытаетесь получить доступ к данным в универсальном контейнере.

5 голосов
/ 10 марта 2015

Предупреждение подавления не является решением. Вы не должны делать двухуровневое приведение в одном утверждении.

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {

    // first, cast the returned Object to generic HashMap<?,?>
    HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");

    // next, cast every entry of the HashMap to the required type <String, String>
    HashMap<String, String> returingHash = new HashMap<>();
    for (Entry<?, ?> entry : theHash.entrySet()) {
        returingHash.put((String) entry.getKey(), (String) entry.getValue());
    }
    return returingHash;
}
2 голосов
/ 14 октября 2010

Возьмите этот, это намного быстрее, чем создание нового HashMap, если он уже один, но все же безопасный, так как каждый элемент проверяется на соответствие его типу

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }
2 голосов
/ 04 февраля 2009

Возможно, я неправильно понял вопрос (пример и пара окружающих строк были бы хорошими), но почему вы не всегда используете соответствующий интерфейс (и Java5 +)? Я не вижу причин, по которым вы бы хотели привести к HashMap вместо Map<KeyType,ValueType>. На самом деле, я не могу себе представить любую причину для установки типа переменной HashMap вместо Map.

А почему источник Object? Это тип параметра устаревшей коллекции? Если это так, используйте дженерики и укажите нужный тип.

2 голосов
/ 04 февраля 2009

Если мне нужно использовать API, который не поддерживает Generics. Я пытаюсь изолировать эти вызовы в процедурах-оболочках с как можно меньшим количеством строк. Затем я использую аннотацию SuppressWarnings и одновременно добавляю приведение типов безопасности.

Это просто личное предпочтение держать вещи как можно более аккуратными.

2 голосов
/ 04 февраля 2009

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

HashMap<String, Object> test = new HashMap();

, который выдаст предупреждение, когда вам нужно будет

HashMap<String, Object> test = new HashMap<String, Object>();

возможно, стоит посмотреть на

Обобщения в языке программирования Java

если вы не знакомы с тем, что нужно сделать.

1 голос
/ 30 января 2015

Вот один из способов справиться с этим, когда я переопределяю операцию equals().

public abstract class Section<T extends Section> extends Element<Section<T>> {
    Object attr1;

    /**
    * Compare one section object to another.
    *
    * @param obj the object being compared with this section object
    * @return true if this section and the other section are of the same
    * sub-class of section and their component fields are the same, false
    * otherwise
    */       
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            // this exists, but obj doesn't, so they can't be equal!
            return false;
        }

        // prepare to cast...
        Section<?> other;

        if (getClass() != obj.getClass()) {
            // looks like we're comparing apples to oranges
            return false;
        } else {
            // it must be safe to make that cast!
            other = (Section<?>) obj;
        }

        // and then I compare attributes between this and other
        return this.attr1.equals(other.attr1);
    }
}

Кажется, это работает в Java 8 (даже скомпилировано с -Xlint:unchecked)

1 голос
/ 04 февраля 2009

Почти каждая проблема в области компьютерных наук может быть решена путем добавления уровня косвенности * или чего-то еще.

Итак, представьте неуниверсальный объект более высокого уровня, чем Map. Без контекста это не будет выглядеть очень убедительно, но в любом случае:

public final class Items implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private Map<String,String> map;
    public Items(Map<String,String> map) {
        this.map = New.immutableMap(map);
    }
    public Map<String,String> getMap() {
        return map;
    }
    @Override public String toString() {
        return map.toString();
    }
}

public final class New {
    public static <K,V> Map<K,V> immutableMap(
        Map<? extends K, ? extends V> original
    ) {
        // ... optimise as you wish...
        return Collections.unmodifiableMap(
            new HashMap<String,String>(original)
        );
    }
}

static Map<String, String> getItems(HttpSession session) {
    Items items = (Items)
        session.getAttribute("attributeKey");
    return items.getMap();
}

* За исключением слишком большого количества уровней косвенности.

0 голосов
/ 12 июня 2010

Если вы уверены, что типом, возвращаемым session.getAttribute (), является HashMap, то вы не можете типизировать этот точный тип, а полагаться только на проверку универсального HashMap

HashMap<?,?> getItems(javax.servlet.http.HttpSession session) {  
    HashMap<?,?> theHash = (HashMap<?,?>)session.getAttribute("attributeKey");
    return theHash;
} 

В этом случае Eclipse будет вызывать неожиданные предупреждения, но, конечно, это может привести к ошибкам во время выполнения, которые сложно отладить. Я использую этот подход только в некритическом контексте.

0 голосов
/ 04 февраля 2009

Просто проверь его, прежде чем разыграть.

Object someObject = session.getAttribute("attributeKey");
if(someObject instanceof HashMap)
HashMap<String, String> theHash = (HashMap<String, String>)someObject;  

И для всех, кто спрашивает, довольно часто можно получать объекты, в которых вы не уверены, какого типа. Множество устаревших реализаций "SOA" передают различные объекты, которым вы не всегда должны доверять. (Ужасы!)

РЕДАКТИРОВАТЬ Один раз изменил пример кода, чтобы он соответствовал обновлениям автора, и после некоторых комментариев я вижу, что instanceof не очень хорошо работает с дженериками. Однако изменение проверки для проверки внешнего объекта, похоже, хорошо работает с компилятором командной строки. Теперь опубликован пересмотренный пример.

...