Як звернутися до попереджених попереджень про викиди?


611

Затьмарення попереджає мене про таку форму:

Безпека типу: Незареєстрована передача від Object до 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.

Якщо ви використовуєте HttpSession таким чином, ознайомтеся зі статтею Брайана Геца на тему: ibm.com/developerworks/library/j-jtp09238.html
Том Хоутін - смуга

Якщо неперевірений виступ неминучий, хороша ідея - це щільно поєднати його з чимось, що логічно представляє його тип (наприклад, enumабо навіть екземпляри Class<T>), так що ви можете подивитися на нього і знати, що це безпечно.
Філіп Гін



Я б додав, я виявив, що можу додати лише @SuppressWarnings ("невірно") на рівні методу, який містить код-порушення. Тож я перебив код у звичайний режим, де мені довелося це зробити. Я завжди думав, що ти можеш це зробити безпосередньо над відповідним рядком.
JGFMK

Відповіді:


557

Очевидна відповідь, звичайно, не робити неперевірену ролю.

Якщо це абсолютно необхідно, то, принаймні, спробуйте обмежити область @SuppressWarningsанотації. Відповідно до його Javadocs , він може працювати на локальних змінних; таким чином, це навіть не впливає на весь метод.

Приклад:

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

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


112
+1, щоб вказати, що він може мати місцеві змінні. Eclipse пропонує лише додати його до всього методу ...
thSoft

17
Eclipse 3.7 (Indigo) підтримує додавання неперевірених до локальних змінних.
sweetfa

78
Попередження не лише тому, що компілятор не знає, що амплуа є безпечним. Наприклад, String s = (String) new Object() ;не отримує жодного попередження, навіть якщо компілятор не знає, що трансляція є безпечною. Попередження полягає в тому, що компілятор (a) не знає, що команда є безпечною. І (b) не буде генерувати повну перевірку виконання в точці виступу. Буде перевірка того, що це Hashmap, але не буде перевірки, що це а HashMap<String,String>.
Теодор Норвелл

9
На жаль, незважаючи на те, що вступ і попередження призначені для призначення , анотація повинна надходити на змінну декларацію ... Тож якщо оголошення та призначення знаходяться в різних місцях (скажімо, зовні та всередині блоку "спробувати" відповідно) , Eclipse тепер генерує два попередження: оригінальний неперевірений ролик та нову діагностику "зайвих приміток".
Ti Strga

6
Приблизним варіантом для примітки, яка потребує супроводу декларації локальної змінної, яка може знаходитись в іншому обсязі в іншому рядку, ніж фактичний формат, полягає у створенні локальної змінної в межах виступу, спеціально для виконання трансляції в тому ж рядку як декларація. Потім призначте цю змінну фактичній змінній, яка знаходиться в іншій області. Це метод, який я також використовував для придушення попередження про відступ до змінної екземпляра, оскільки анотація також не може бути застосована тут.
Джефф Локхарт

168

На жаль, тут немає великих варіантів. Пам'ятайте, мета всього цього - зберегти безпеку типу. " Java Generics " пропонує рішення для роботи з негенерізованими застарілими бібліотеками, і в розділі 8.2, зокрема, називається "техніка порожнього циклу". В основному, зробіть небезпечний склад і придушіть попередження. Потім прокрутіть карту так:

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

Якщо виникне несподіваний тип, ви отримаєте час виконання ClassCastException, але принаймні це станеться близько до джерела проблеми.


6
Набагато краща відповідь, ніж та, яку надає skiphoppy з кількох причин: 1) Цей код набагато, набагато коротший. 2) Цей код фактично кидає ClassCastException, як очікувалося. 3) Цей код не є повною копією вихідної карти. 4) Цикли можуть бути легко обгорнуті в окремий метод, який використовується в асперті, що легко видалить показник продуктивності у виробничому коді.
Штійн де Вітт

6
Чи не існує можливості компілятор Java або компілятор JIT вирішити, що результати цього коду не використовуються та "оптимізують" його, не компілюючи його?
RenniePet

1
Це насправді не мертвий код, якщо він потенційно може кинути виняток. Я не знаю достатньо про компілятори JIT, які використовуються сьогодні, щоб гарантувати, що жоден з них не зіпсує це, але я почуваю себе досить впевнено, кажучи, що цього не слід робити.
GrandOpener

3
Це все ще не гарантує безпеку типу, оскільки та сама карта все ще використовується. Спочатку це може бути визначено як Карта <Об'єкт, Об'єкт>, що, як правило, містить рядки та числа, а потім, якщо буде додано булевий, то користувач цього коду отримає заплутане та досить важко відстежити сюрприз. Єдиний спосіб гарантувати безпеку типу - скопіювати його на нову карту із запитаним типом, який гарантує те, що дозволяється входити в неї.
user2219808

112

Ого; Я думаю, я зрозумів відповідь на власне запитання. Я просто не впевнений, що воно того варте! :)

Проблема в тому, що акторський склад не перевіряється. Отже, ви повинні перевірити це самостійно. Ви не можете просто перевірити параметризований тип за допомогою 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? Є щось краще, що я міг би кинути? Чи варто зробити це перевіреним винятком?


68
цей матеріал заплутаний, але я думаю, що все, що ви зробили, торгується ClassCastExceptions для AssertionErrors.
Дастін Гец

59
Чувак, це точно не варто! Уявіть собі, бідний сік, який повинен повернутися та змінити якийсь код із цим безладом. Мені не подобається придушувати попередження, але я думаю, що тут менше зла.
Крейг В

69
Це не просто потворне, заплутане безладдя (коли ви не можете уникнути одного рясного зауваження, може пройти програміста з технічного обслуговування); ітерація над кожним елементом колекції перетворює команду з операції O (1) на операцію O (n). Це те, чого ніколи не можна було б очікувати і може легко перетворитися на жахливе загадкове уповільнення.
Ден піднімається Firelight

22
@DanNeely ви праві. Взагалі, ніхто ніколи цього не повинен робити.
skiphoppy

4
Деякі зауваження ... підпис методу невірний, оскільки він не "кидає" прокляту річ, він просто копіює існуючу карту на нову карту. Крім того, можливо, це буде відновлено для прийняття будь-якої карти, а не покладатися на сам HashMap (тобто взяти Map і повернути Map у підписі методу, навіть якщо внутрішній тип - HashMap). Вам не потрібно робити кастинг або зберігання на новій карті - якщо ви не кинете помилку твердження, то дана карта має правильні типи всередині неї на даний момент. Створювати нову карту із загальними типами безглуздо, оскільки ви все ще можете зробити її сирою та поставити будь-що.
MetroidFan2002

51

У налаштуваннях Eclipse перейдіть до Java-> Компілятор-> Помилки / Попередження-> Загальні типи та поставте Ignore unavoidable generic type problemsпрапорець.

Це задовольняє наміри питання, тобто

Я хотів би уникати попереджень про затемнення ...

якщо не дух.


1
Ах, дякую за це :) Я отримував uses unchecked or unsafe operations.помилку javac, але "" додав @SuppressWarnings("unchecked")Eclipse нещасним, стверджуючи, що придушення було непотрібним. Якщо зняти цей прапорець, змушує Eclipse і javacвести себе так само, чого я хотів. Явне придушення попередження в коді набагато чіткіше, ніж придушення його скрізь всередині Eclipse.
dimo414

26

Ви можете створити клас утиліти, як описано нижче, і використовувати його для придушення неперевіреного попередження.

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


18
не зволікаючи, але обгортка точно не додає нічого, а лише придушує попередження.
Дастін Гец

3
+1, оскільки це рішення не витрачає дорогоцінні кодові рядки.
Тіно

1
@ErikE Занадто багато. Набагато дорожчі монітори великого розміру і більшої роздільної здатності, щоб дати місце всім цим марнотратним лініям, більший стіл для розміщення всіх великих моніторів, більший зал для розміщення великого столу та проникливий начальник ..
Тіно,

1
@ErikE Прокрутки, для vi? Ви жартуєте?
Тіно

21

Цей матеріал важкий, але ось мої поточні думки:

Якщо ваш API поверне Object, ви нічого не можете зробити - незалежно від того, ви будете сліпо кидати об'єкт. Ви дозволяєте Java кидати ClassCastExceptions, або ви можете перевірити кожен елемент самостійно і викинути твердження або IllegalArgumentExceptions або щось подібне, але ці перевірки часу виконання рівноцінні. Ви повинні придушити неперевірений час компіляції незалежно від того, що ви робите під час виконання.

Я просто віддаю перевагу сліпому перегляду і дозвольте JVM виконувати перевірку виконання для мене, оскільки ми "знаємо", що повинен повернути API, і зазвичай готові припустити, що API працює. Використовуйте дженерики скрізь над актором, якщо вони вам потрібні. Ви насправді нічого там не купуєте, оскільки у вас все ще є єдиний сліпий актерський склад, але принаймні ви можете використовувати дженерики звідти вгору, щоб JVM допоможе вам уникнути сліпих ролей в інших частинах вашого коду.

У цьому конкретному випадку, імовірно, ви можете бачити виклик на SetAttribute і бачити, який тип відбувається, тому просто сліпе виведення типу на той самий на виході не є аморальним. Додайте коментар із посиланням на SetAttribute і виконайте його.


16

Ось скорочений приклад, який дозволяє уникнути попередження про "неперевірену роль" , використовуючи дві стратегії, зазначені в інших відповідях.

  1. Передайте Клас типу цікавлять як параметр під час виконання ( Class<T> inputElementClazz). Тоді ви можете використовувати:inputElementClazz.cast(anyObject);

  2. Для типового лиття колекції використовувати підстановку? замість загального типу T, щоб визнати, що ви дійсно не знаєте, яких об’єктів очікувати від застарілого коду ( Collection<?> unknownTypeCollection). Зрештою, це те, що нам хоче сказати застереження "неперевіреного виклику": Ми не можемо бути впевнені, що отримаємо Collection<T>, тому чесна річ - це використовувати Collection<?>. Якщо це абсолютно необхідно, колекцію відомого типу все ж можна створити ( Collection<T> knownTypeCollection).

Спадковий код, пов'язаний у наведеному нижче прикладі, має атрибут "input" у StructuredViewer (StructuredViewer - віджет дерева або таблиці, "input" - модель даних за ним). Цей "вхід" може бути будь-яким видом Java Collection.

public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
    IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
    // legacy code returns an Object from getFirstElement,
    // the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
    T firstElement = inputElementClazz.cast(selection.getFirstElement());

    // legacy code returns an object from getInput, so we deal with it as a Collection<?>
    Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();

    // for some operations we do not even need a collection with known types
    unknownTypeCollection.remove(firstElement);

    // nothing prevents us from building a Collection of a known type, should we really need one
    Collection<T> knownTypeCollection = new ArrayList<T>();
    for (Object object : unknownTypeCollection) {
        T aT = inputElementClazz.cast(object);
        knownTypeCollection.add(aT);
        System.out.println(aT.getClass());
    }

    structuredViewer.refresh();
}

Звичайно, наведений вище код може створювати помилки під час виконання, якщо ми використовуємо застарілий код із неправильними типами даних (наприклад, якщо ми встановимо масив як "вхід" StructuredViewer замість колекції Java).

Приклад виклику методу:

dragFinishedStrategy.dragFinished(viewer, Product.class);

13

У світі HTTP Session ви справді не можете уникнути кастингу, оскільки 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Пізніше ніяких несподіваних .


Це дійсно корисна відповідь.
GPrathap

10

Якщо ви хочете відключити перевірку, в Android Studio ви можете скористатися:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

2
Це працює і в IntelliJ IDE
neXus

8

У цьому конкретному випадку я б не зберігав Карти у HttpSession безпосередньо, а натомість екземпляр власного класу, який, у свою чергу, містить Map (деталі реалізації класу). Тоді ви можете бути впевнені, що елементи на карті потрібного типу.

Але якщо ви все-таки хочете перевірити правильність вмісту Карти, ви можете використовувати такий код:

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);
    }
}

1
Чудовий; Я думаю, що можу поєднати це зі своєю відповіддю, щоб параметризувати його та уникати загального придушення попереджень!
skiphoppy

1
+1, мабуть, найкращий рецепт (простий для розуміння та підтримки), щоб зробити це безпечно з перевірками часу виконання
Tino

8

Функція утиліти Objects.Uncked у відповіді вище Еско Луонтола - це чудовий спосіб уникнути неполадок у програмі.

Якщо ви не хочете, щоб програма 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

Попередження придушення не є рішенням. Ви не повинні робити два рівні кастинги в одному висловлюванні.

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;
}

1
Його п’ятирічне запитання? Вам потрібно так багато працювати? Враховуючи стирання типу Java, друга хеш-карта повинна бути ідентичною першій під час виконання; Я думаю, що було б ефективніше і уникати копії, якби ви просто повторили записи і перевірили, що вони всі екземпляри рядків. Або TBH, перевірте джерело JAR сервлета, який ви використовуєте, і переконайтеся, що він лише коли-небудь містить рядки.
Rup

1
По сьогодні я бачу це попередження в проектах. Його проблемою була не перевірка типу, а попередження, спричинене "введенням" у непрограшенную карту.
абас

2

Швидкий здогад, якщо ви опублікували свій код, можна сказати точно, але, можливо, ви щось зробили в порядку

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

який видасть попередження, коли вам потрібно це зробити

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

це, можливо, варто придивитись

Загальна мова в мові програмування Java

якщо ваш незнайомий з тим, що потрібно зробити.


1
На жаль, ситуація не така проста. Додано код.
skiphoppy

1
Я прийшов сюди шукати відповідь на дещо іншу проблему: і ти мені точно сказав, що мені потрібно! Дякую!
статистика

2

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

І чому джерело Object? Це тип параметра застарілої колекції? Якщо так, використовуйте дженерики та вкажіть потрібний тип.


2
Я майже впевнений, що перехід на Map у цьому випадку нічого не змінить, але дякую за пораду програмування, яка може змінити спосіб я робити якісь речі, на краще. Джерело об'єкта - API, над яким я не маю (доданий код).
skiphoppy

2

Якщо мені доведеться використовувати API, який не підтримує Generics .. Я намагаюся виділити ці виклики в обгортковій програмі якомога менше рядків. Тоді я використовую примітку SuppressWarnings, а також додаю одночасно типи безпеки безпеки.

Це лише особиста перевага, щоб зберегти речі максимально акуратними.


2

Візьміть цей, це набагато швидше, ніж створити новий 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);
    }

key.isAssignableFrom(e.getKey().getClass())можна записати якkey.isInstance(e.getKey())
user102008

1

Просто введіть прапорець перед тим, як ви її передали.

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

А для тих, хто запитує, досить часто приймати об’єкти, де ви не впевнені у своєму роді. Багато застарілих реалізацій SOA проходять навколо різних об'єктів, яким не завжди слід довіряти. (Жахи!)

EDIT Змінив приклад коду один раз, щоб він відповідав оновленням плаката, і, слідуючи деяким коментарям, я бачу, що instanceof не чудово грає з generics. Однак зміна чека для перевірки зовнішнього об'єкта, здається, добре грає з компілятором командного рядка. Змінений приклад тепер розміщено.


8
На жаль, дженерики роблять це неможливим. Це не просто HashMap, це HashMap з інформацією про тип. І якщо я усуну цю інформацію, я просто підштовхую попередження до інших місць.
skiphoppy

1

Практично кожну проблему з інформатики можна вирішити, додавши рівень непрямості *, або щось таке.

Отже, введіть негенерований об’єкт, який є вищого рівня, ніж а 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();
}

* За винятком забагато рівнів непрямості.


1
Цитата приписується покійному професору Девіду Уілеру. en.wikipedia.org/wiki/…
Стівен C

1

Ось один із способів вирішити це, коли я перекриваю 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)


0

Якщо ви впевнені, що тип, що повертається session.getAttribute (), - це HashMap, ви не можете вводити цей точний тип, а покладаєтесь лише на перевірку загального HashMap

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

Eclipse потім здивує попередження, але, звичайно, це може призвести до помилок виконання, які важко налагодити. Я використовую такий підхід лише в не критичних для експлуатації контекстах.


0

Два способи, один, який повністю уникає тегу, а інший, використовуючи неслухняний, але приємний корисний метод.
Проблема полягає у попередніх узагальнених колекціях ...
Я вважаю, що головне правило полягає в тому, що "кидати об'єкти по одній речі" - що це означає при спробі використовувати необроблені класи в узагальненому світі, це тому, що ви не знаєте, що є в цій карті <?,?> (і справді JVM навіть може виявити, що це навіть не карта!), коли ви думаєте про це, очевидно, що ви не можете її віддати. Якщо у вас була карта <String,?> Map2, то HashSet <String> keys = (HashSet <String>) map2.keySet () не дає вам попередження, незважаючи на те, що це "акт віри" для компілятора (оскільки це може виявитися TreeSet) ... але це лише один акт віри.

PS на заперечення про те, що повторення, як по-моєму, "нудне" і "вимагає часу", відповідь - "не біль, ні виграш": узагальнена колекція гарантовано містить Map.Entry <String, String> s, і нічого ще. Ви повинні заплатити за цю гарантію. При систематичному використанні дженериків ця оплата, красиво, приймає форму відповідності кодування, а не машинного часу!
Одна школа думок може сказати, що вам слід встановити параметри Eclipse, щоб робити такі неперевірені помилки, а не попередження. У такому випадку вам доведеться скористатися моїм першим способом.

package scratchpad;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

public class YellowMouse {

    // First way

    Map<String, String> getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) {
      Map<?, ?> theHash = (Map<?, ?>)session.getAttribute("attributeKey");

      Map<String, String> yellowMouse = new HashMap<String, String>();
      for( Map.Entry<?, ?> entry : theHash.entrySet() ){
        yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() );
      }

      return yellowMouse;
    }


    // Second way

    Map<String, String> getHashMapUsingNaughtyButNiceUtilityMethod(HttpSession session) {
      return uncheckedCast( session.getAttribute("attributeKey") );
    }


    // NB this is a utility method which should be kept in your utility library. If you do that it will
    // be the *only* time in your entire life that you will have to use this particular tag!!

    @SuppressWarnings({ "unchecked" })
    public static synchronized <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }


}

той факт, що у вас немає привілеїв для коментарів, не дозволяє редагувати відповіді інших людей, щоб додавати свої коментарі; ви редагуєте відповіді інших людей, щоб вдосконалити їх у форматуванні, синтаксисі, ..., не додаючи до них своєї думки. Коли ви досягнете 50 повторень, ви зможете коментувати всюди, тим часом я впевнений, що ви зможете чинити опір (або, якщо ви справді не можете, написати свої коментарі до існуючих відповідей у ​​своєму дописі). (зауважте для інших: я пишу це тому, що побачив - і відхилив - запропоновані ним коментарі-редагування до інших публікацій в засобах модерації)
Matteo Italia

-1

Це змушує попередження відходити ...

 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
Ні, це не так. Власне, це створює два попередження, де спочатку було одне.
Штійн де Вітт

А, добре. Не впевнений, чому я так думав.
lukewm

-3

Рішення. Вимкніть це попередження у програмі Eclipse. Не @SuppressWarnings, просто відключіть його повністю.

Деякі з "рішень", представлених вище, виходять із строку, роблячи код нечитабельним заради придушення дурного попередження.


9
Чи можу я запитати, чому? Глобальне відключення попередження приховає інші місця, де ця проблема реальна. додавання @SuppressWarningsкоду взагалі не робить код нечитабельним.
MByD
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.