Как я обращаюсь к непроверенным предупреждениям броска?

558
задан Chris Stillwell 28 March 2017 в 21:27
поделиться

14 ответов

Ничего себе; я думаю, что выяснил ответ на свой собственный вопрос. Я просто не уверен, что это стоит того! :)

проблемой является бросок, не проверяется. Так, необходимо проверить его сами. Вы не можете только проверить параметризованный тип с instanceof, потому что параметризованная информация о типе недоступна во времени выполнения, будучи стертым во время компиляции.

, Но, можно выполнить проверку на каждом объекте в хеше с instanceof, и при этом, можно создать новый хеш, который безопасен с точки зрения типов. И Вы не вызовете предупреждений.

Благодаря mmyers и Esko Luontola, я параметризовал код, который я первоначально написал здесь, таким образом, он может оборачиваться в служебном классе где-нибудь и использоваться для любого, параметризовал HashMap. Если Вы хотите понять это лучше и не очень знакомы с дженериками, я поощряю просматривать историю редактирования этого ответа.

public static <K, V> HashMap<K, V> castHash(HashMap input,
                                            Class<K> keyClass,
                                            Class<V> valueClass) {
  HashMap<K, V> output = new HashMap<K, V>();
  if (input == null)
      return output;
  for (Object key: input.keySet().toArray()) {
    if ((key == null) || (keyClass.isAssignableFrom(key.getClass()))) {
        Object value = input.get(key);
        if ((value == null) || (valueClass.isAssignableFrom(value.getClass()))) {
            K k = keyClass.cast(key);
            V v = valueClass.cast(value);
            output.put(k, v);
        } else {
            throw new AssertionError(
                "Cannot cast to HashMap<"+ keyClass.getSimpleName()
                +", "+ valueClass.getSimpleName() +">"
                +", value "+ value +" is not a "+ valueClass.getSimpleName()
            );
        }
    } else {
        throw new AssertionError(
            "Cannot cast to HashMap<"+ keyClass.getSimpleName()
            +", "+ valueClass.getSimpleName() +">"
            +", key "+ key +" is not a " + keyClass.getSimpleName()
        );
    }
  }
  return output;
}

Это - большая работа, возможно для очень небольшого количества вознаграждения... Я не уверен, буду ли я использовать его или нет. Я ценил бы любые комментарии относительно того, думают ли люди, что это стоит того или нет. Кроме того, я ценил бы предложения улучшения: есть ли что-то лучше, что я могу сделать помимо броска AssertionErrors? Есть ли что-то лучше, что я мог бросить? Я должен сделать это контролируемой исключительной ситуацией?

110
ответ дан informatik01 29 March 2017 в 07:27
поделиться

Очевидный ответ, конечно, не должен делать броска непроверенного.

, Если это абсолютно необходимо, затем по крайней мере, пытаются ограничить объем @SuppressWarnings аннотация. Согласно Javadocs, это может пойти на локальные переменные; этим путем это даже не влияет на весь метод.

Пример:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

нет никакого способа определить, должен ли Map действительно иметь универсальные параметры <String, String>. Необходимо знать заранее, чем параметры должны быть (или Вы узнаете, когда Вы доберетесь ClassCastException). Поэтому код генерирует предупреждение, потому что компилятор не может возможно знать, безопасно ли.

520
ответ дан Michael Myers 29 March 2017 в 07:27
поделиться

Просто typecheck это перед кастингом его.

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

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

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

0
ответ дан Rick 29 March 2017 в 07:27
поделиться

Проблема заключается в здесь:

... = (HashMap<String, String>)session.getAttribute("attributeKey");

результат session.getAttribute(...) object, который мог быть чем-либо, но так как Вы "знаете", что это HashMap<String, String>, Вы просто бросаете, не проверяя его сначала. Таким образом, предупреждение. Чтобы быть педантичными, которым Java хочет, чтобы Вы были в этом случае, необходимо получить результат и проверить, что это - совместимость с instanceof.

-1
ответ дан JMD 29 March 2017 в 07:27
поделиться

Почти каждая проблема в Информатике может быть решена путем добавления уровня абстракции*, или что-то.

Так представляют неродовой объект, который имеет более высокий уровень что 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();
}

*Except слишком много уровней абстракции.

1
ответ дан Tom Hawtin - tackline 29 March 2017 в 07:27
поделиться

Быстрое предположение при регистрации кода, может сказать наверняка, но Вы, возможно, сделали что-то вроде

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

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

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

, на это могло бы стоить посмотреть

Дженерики на Языке программирования Java

если незнакомое с какой потребности быть сделанным.

2
ответ дан Mark Davidson 29 March 2017 в 07:27
поделиться

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

И почему источник Object? Действительно ли это - тип параметра набора прежней версии? Если так, используйте дженерики и укажите тип, который Вы хотите.

2
ответ дан phihag 29 March 2017 в 07:27
поделиться

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

, Но если Вы так или иначе хотите проверить, что содержание Карты имеет правильный тип, Вы могли использовать код как это:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("a", 1);
    map.put("b", 2);
    Object obj = map;

    Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
    Map<String, String> error = safeCastMap(obj, String.class, String.class);
}

@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
    checkMap(map);
    checkMapContents(keyType, valueType, (Map<?, ?>) map);
    return (Map<K, V>) map;
}

private static void checkMap(Object map) {
    checkType(Map.class, map);
}

private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        checkType(keyType, entry.getKey());
        checkType(valueType, entry.getValue());
    }
}

private static <K> void checkType(Class<K> expectedType, Object obj) {
    if (!expectedType.isInstance(obj)) {
        throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
    }
}
8
ответ дан Esko Luontola 29 March 2017 в 07:27
поделиться

В мире Сеанса HTTP Вы не можете действительно избежать броска, так как API записан, тот путь (берет и возвраты только Object).

С определенной работой можно легко избежать броска непроверенного, 'все же. Это означает, что превратится в традиционный бросок, дающий ClassCastException тут же в случае ошибки). Исключение непроверенное могло превратиться CCE в любой точке позже вместо точки броска (это - причина, почему это - отдельное предупреждение).

Замена HashMap со специализированным классом:

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Attributes extends AbstractMap<String, String> {
    final Map<String, String> content = new HashMap<String, String>();

    @Override
    public Set<Map.Entry<String, String>> entrySet() {
        return content.entrySet();
    }

    @Override
    public Set<String> keySet() {
        return content.keySet();
    }

    @Override
    public Collection<String> values() {
        return content.values();
    }

    @Override
    public String put(final String key, final String value) {
        return content.put(key, value);
    }
}

Затем бросок к тому классу вместо Map<String,String> и все будет проверено в точном месте, где Вы пишете свой код. Никакое неожиданное ClassCastExceptions позже.

12
ответ дан Joachim Sauer 29 March 2017 в 07:27
поделиться

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

public class Objects {

    /**
     * Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }
}

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

import static Objects.uncheckedCast;
...

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

еще Некоторая дискуссия об этом здесь: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html

26
ответ дан Esko Luontola 29 March 2017 в 07:27
поделиться

К сожалению, здесь нет никаких больших опций. Помните, цель всего этого состоит в том, чтобы сохранить безопасность типов". Дженерики Java " предложения решения для контакта с non-genericized библиотеками прежней версии, и существует та, в особенности названная "пустым методом цикла" в разделе раздела 8.2. В основном сделайте небезопасный бросок и подавите предупреждение. Затем цикл через карту как это:

@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());

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

147
ответ дан Ziem 29 March 2017 в 07:27
поделиться

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

Это - просто персональное предпочтение для хранения вещей максимально аккуратными.

2
ответ дан Fortyrunner 29 March 2017 в 07:27
поделиться

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

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

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

0
ответ дан 22 November 2019 в 22:07
поделиться

Благодаря этому предупреждения исчезают...

 static Map<String, String> getItems(HttpSession session) {
        HashMap<?, ?> theHash1 = (HashMap<String,String>)session.getAttribute("attributeKey");
        HashMap<String,String> theHash = (HashMap<String,String>)theHash1;
    return theHash;
}
-1
ответ дан 22 November 2019 в 22:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: