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

Eclipse дает мне предупреждение о следующей форме:

Тип безопасности: снятие флажка с объекта на HashMap

Это от вызова API, который у меня нет контроля над возвращаемым объектом Object:

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

Я бы хотел избежать предупреждений Eclipse, если это возможно, поскольку теоретически они указывают, по крайней мере, потенциальную проблему с кодом. Тем не менее, я еще не нашел хороший способ устранить эту проблему. Я могу извлечь единственную строку, @SuppressWarnings("unchecked") с методом самостоятельно, и добавить к этому методу @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 should be parameterized. 

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

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

Пример:

 @SuppressWarnings("unchecked") Map myMap = (Map) deserializeMap(); 

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

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

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

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

Вау; Думаю, я понял ответ на свой вопрос. Я просто не уверен, что это того стоит! 🙂

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

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

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

 public static  HashMap castHash(HashMap input, Class keyClass, Class valueClass) { HashMap output = new HashMap(); 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; } 

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

В настройках Eclipse перейдите в Java-> Compiler-> Errors / Warnings-> Generic types и установите флажок « Ignore unavoidable generic type problems .

Это удовлетворяет цели вопроса, т.е.

Я бы хотел избежать предупреждений Eclipse …

если не дух.

Вы можете создать class утилиты следующим образом и использовать его для подавления непроверенного предупреждения.

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

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

 import static Objects.uncheckedCast; ... HashMap getItems(javax.servlet.http.HttpSession session) { return uncheckedCast(session.getAttribute("attributeKey")); } 

Более подробно об этом можно прочитать здесь: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html

Это сложно, но вот мои текущие мысли:

Если ваш API возвращает Object, то вы ничего не можете сделать – независимо от того, вы будете слепо бросать объект. Вы позволяете Java вызывать ClassCastExceptions, или вы можете проверить каждый элемент самостоятельно, а также вызывать утверждения или IllegalArgumentExceptions или некоторые из них, но эти проверки времени выполнения эквивалентны. Вы должны подавлять время неконтролируемого выполнения компиляции независимо от того, что вы делаете во время выполнения.

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

В этом конкретном случае, по-видимому, вы можете увидеть вызов SetAttribute и увидеть, как происходит тип, поэтому просто слепое отбрасывание типа к тому же на выходе не является аморальным. Добавьте комментарий, ссылающийся на SetAttribute, и сделайте это.

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

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

Замените HashMap специальным classом:

 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 { final Map content = new HashMap(); @Override public Set> entrySet() { return content.entrySet(); } @Override public Set keySet() { return content.keySet(); } @Override public Collection values() { return content.values(); } @Override public String put(final String key, final String value) { return content.put(key, value); } } 

Затем отбрасываем этот class вместо Map и все будет проверяться в том месте, где вы пишете свой код. Никаких неожиданных ClassCastExceptions позже.

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

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

 public static void main(String[] args) { Map map = new HashMap(); map.put("a", 1); map.put("b", 2); Object obj = map; Map ok = safeCastMap(obj, String.class, Integer.class); Map error = safeCastMap(obj, String.class, String.class); } @SuppressWarnings({"unchecked"}) public static  Map safeCastMap(Object map, Class keyType, Class valueType) { checkMap(map); checkMapContents(keyType, valueType, (Map) map); return (Map) map; } private static void checkMap(Object map) { checkType(Map.class, map); } private static  void checkMapContents(Class keyType, Class valueType, Map map) { for (Map.Entry entry : map.entrySet()) { checkType(keyType, entry.getKey()); checkType(valueType, entry.getValue()); } } private static  void checkType(Class expectedType, Object obj) { if (!expectedType.isInstance(obj)) { throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj); } } 

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

  1. Передайте class типа интереса в качестве параметра во время выполнения ( Class inputElementClazz ). Затем вы можете использовать: inputElementClazz.cast(anyObject);

  2. Для литья типов коллекции используйте групповой символ? вместо общего типа T, чтобы признать, что вы действительно не знаете, какие объекты ожидать от устаревшего кода ( Collection unknownTypeCollection ). В конце концов, это то, о чем предупреждает предупреждение «unchecked cast»: Мы не можем быть уверены, что получим Collection , поэтому честно, что нужно использовать Collection . Если это абсолютно необходимо, можно собрать коллекцию известного типа ( Collection knownTypeCollection ).

Унаследованный код, связанный в приведенном ниже примере, имеет атрибут «вход» в StructuredViewer (StructuredViewer – это виджет дерева или таблицы, «вход» – это модель данных за ним). Этот «ввод» может представлять собой любую коллекцию Java.

 public void dragFinished(StructuredViewer structuredViewer, Class 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 knownTypeCollection = new ArrayList(); for (Object object : unknownTypeCollection) { T aT = inputElementClazz.cast(object); knownTypeCollection.add(aT); System.out.println(aT.getClass()); } structuredViewer.refresh(); } 

Разумеется, приведенный выше код может дать ошибки времени выполнения, если мы используем устаревший код с неправильными типами данных (например, если мы установим массив как «вход» в StructuredViewer вместо Java Collection).

Пример вызова метода:

 dragFinishedStrategy.dragFinished(viewer, Product.class); 

Функция Objects.Unchecked utility в ответе выше Esko Luontola – отличный способ избежать беспорядка в программе.

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

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

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

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

ПРИМЕЧАНИЕ. Я считаю важным добавить, что иногда предупреждение действительно означает, что вы делаете что-то не так:

 ArrayList intList = new ArrayList(); intList.add(1); Object intListObject = intList; // this line gives an unchecked warning - but no runtime error ArrayList stringList = (ArrayList) intListObject; System.out.println(stringList.get(0)); // cast exception will be given here 

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

В Android Studio, если вы хотите отключить проверку, вы можете использовать:

 //noinspection unchecked Map myMap = (Map) deserializeMap(); 

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

 HashMap 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  HashMap returingHash = new HashMap<>(); for (Entry entry : theHash.entrySet()) { returingHash.put((String) entry.getKey(), (String) entry.getValue()); } return returingHash; } 

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

 HashMap test = new HashMap(); 

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

 HashMap test = new HashMap(); 

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

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

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

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

И почему источником является Object ? Является ли это типом параметров унаследованной коллекции? Если это так, используйте generics и укажите тип, который вы хотите.

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

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

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

 @SuppressWarnings("unchecked") public static  HashMap toHashMap(Object input, Class key, Class 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) input; return new HashMap((Map) input); } 

Просто напишите ее, прежде чем бросать.

 Object someObject = session.getAttribute("attributeKey"); if(someObject instanceof HashMap) HashMap theHash = (HashMap)someObject; 

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

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

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

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

 public final class Items implements java.io.Serializable { private static final long serialVersionUID = 1L; private Map map; public Items(Map map) { this.map = New.immutableMap(map); } public Map getMap() { return map; } @Override public String toString() { return map.toString(); } } public final class New { public static  Map immutableMap( Map original ) { // ... optimise as you wish... return Collections.unmodifiableMap( new HashMap(original) ); } } static Map getItems(HttpSession session) { Items items = (Items) session.getAttribute("attributeKey"); return items.getMap(); } 

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

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

 public abstract class Section extends Element> { 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 )

Проблема здесь:

 ... = (HashMap)session.getAttribute("attributeKey"); 

Результат session.getAttribute(...) – это object который может быть чем угодно, но так как вы «знаете» это HashMap вы просто бросаете, не проверяя его в первую очередь. Таким образом, предупреждение. Чтобы быть педантичным, какой Java хочет, чтобы вы были в этом случае, вы должны получить результат и проверить его совместимость с instanceof .

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

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

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

Два способа: один, который полностью избегает тега, а другой – непослушный, но приятный метод.
Проблема заключается в предварительной генерации коллекций …
Я верю, что эмпирическое правило: «бросать объекты по одной вещи за раз» – это означает, что при попытке использовать сырые classы в обобщенном мире это потому, что вы не знаете, что находится в этой карте ( и даже JVM может даже обнаружить, что это даже не карта!), это очевидно, когда вы думаете об этом, что не можете его отличить. Если у вас была карта Map2, то HashSet keys = (HashSet ) map2.keySet () не дает вам предупреждения, несмотря на то, что это был «акт веры» для компилятора (потому что это может оказаться TreeSet) … но это всего лишь один акт веры.

PS к возражению, что итерация, как на моем первом пути, «скучна» и «требует времени», ответ «без боли нет выгоды»: в обобщенной коллекции будет содержаться Map.Entry 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 getHashMapStudiouslyAvoidingSuppressTag(HttpSession session) { Map theHash = (Map)session.getAttribute("attributeKey"); Map yellowMouse = new HashMap(); for( Map.Entry entry : theHash.entrySet() ){ yellowMouse.put( (String)entry.getKey(), (String)entry.getValue() ); } return yellowMouse; } // Second way Map 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 uncheckedCast(Object obj) { return (T) obj; } } 

This makes the warnings go away…

  static Map getItems(HttpSession session) { HashMap theHash1 = (HashMap)session.getAttribute("attributeKey"); HashMap theHash = (HashMap)theHash1; return theHash; } 

Solution: Disable this warning in Eclipse. Don’t @SuppressWarnings it, just disable it completely.

Several of the “solutions” presented above are way out of line, making code unreadable for the sake of suppressing a silly warning.

  • предупреждение: небезопасный доступ к миру dir / usr / local / bin в PATH, режим 040777
  • Флаги для включения подробных и подробных предупреждений g ++
  • Есть ли способ игнорировать одно предупреждение FindBugs?
  • Предупреждения памяти iPhone OS. Что означают разные уровни?
  • Как найти, какие обещания необработаны в Node.js UnhandledPromiseRejectionWarning?
  • w3c html ошибка проверки - Раздел не имеет заголовка. Рассмотрите возможность использования элементов h2-h6 для добавления идентифицирующих заголовков ко всем разделам
  • В Xcode, как подавлять все предупреждения в определенных исходных файлах?
  • Предупреждение ReSharper - доступ к модифицированному закрытию
  • Отключить предупреждающие сообщения в GCC через файлы заголовков?
  • Как подавлять предупреждения в глобальном масштабе в R Script
  • Предупреждение libpng: iCCP: известный неправильный профиль sRGB
  • Interesting Posts

    Java 8 группировка с использованием пользовательского коллектора?

    Что такое функторы C ++ и их использование?

    Доступ к закладкам Google Chrome с помощью сочетаний клавиш

    Свойство OutputPath не установлено для этого проекта

    Включить ping в брандмауэре Windows 7?

    Где хранятся статические методы и статические переменные на Java?

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

    Как создать несколько папок и называть их, читая строки из текстового файла?

    Где находится Java в Mac OS X?

    Ярлык для «null, если объект имеет значение null, или object.member, если объект не является нулевым»

    Как я могу сосредоточить div Bootstrap с classом spanX?

    Что такое формула excel, которая вернет мне последний номер строки ячейки в пределах диапазона, который содержит конкретные данные?

    Есть ли способ настроить папку по умолчанию для Windows Explorer?

    Бесплатное приложение для удаленного рабочего стола полезно для работы с кем-то компьютером безграмотным?

    Как сделать Xcode найти файл FacebookSDK.h?

    Давайте будем гением компьютера.