【问题标题】:How do I address unchecked cast warnings?如何解决未经检查的强制转换警告?
【发布时间】:2010-10-05 06:38:15
【问题描述】:

Eclipse 向我发出以下形式的警告:

类型安全:从 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,请查看 Brian Goetz 关于该主题的文章:ibm.com/developerworks/library/j-jtp09238.html
  • 如果一个未经检查的演员表是不可避免的,一个好主意是将它与逻辑上表示它的类型的东西(如enum甚至Class&lt;T&gt;的实例)紧密结合,这样你就可以看一眼它并且知道它是安全的。
  • 相关/欺骗:Type safety: Unchecked cast
  • 我会添加,我发现我只能在包含违规代码的方法级别添加 @SuppressWarnings("unchecked") 。因此,我将代码分解为必须执行此操作的例程。我一直认为您可以在相关行的上方立即执行此操作。

标签: java generics warnings


【解决方案1】:

当然,显而易见的答案是不要进行未经检查的演员表。

如果绝对有必要,那么至少尝试限制@SuppressWarnings 注释的范围。根据它的Javadocs,可以上局部变量;这样,它甚至不会影响整个方法。

例子:

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

没有办法确定Map 是否真的应该具有泛型参数&lt;String, String&gt;。您必须事先知道参数应该是什么(或者您会在收到ClassCastException 时发现)。这就是代码生成警告的原因,因为编译器不可能知道是否安全。

【讨论】:

  • +1 指出它可以使用局部变量。 Eclipse 只提供将其添加到整个方法中...
  • Eclipse 3.7 (Indigo) 支持添加未选中的局部变量。
  • 警告不仅仅是因为编译器不知道强制转换是安全的。例如String s = (String) new Object() ; 不会收到警告,即使编译器不知道强制转换是安全的。警告是因为编译器 (a) 不知道强制转换是安全的并且 (b) 不会在强制转换点生成完整的运行时检查。将检查它是否为Hashmap,但不会检查它是否为HashMap&lt;String,String&gt;
  • 可悲的是,即使演员表和警告是针对 assignment 的,注释也必须在变量声明中进行......所以如果声明和赋值在在不同的地方(例如,分别在“try”块的外部和内部),Eclipse 现在会生成两个警告:原始的未检查强制转换和新的“不必要的注释”诊断。
  • 对于需要伴随局部变量声明的注释的解决方法,它可能在与实际转换不同的行上的不同范围内,具体是在转换范围内创建一个局部变量在声明的同一行执行转换。然后将此变量分配给不同范围内的实际变量。这也是我用来抑制强制转换为实例变量的警告的方法,因为注释也不能在此处应用。
【解决方案2】:

很遗憾,这里没有很好的选择。请记住,所有这些的目标是保持类型安全。 “Java Generics”提供了一种处理非泛型遗留库的解决方案,在第 8.2 节中有一个特别称为“空循环技术”的解决方案。基本上,进行不安全的演员表,并抑制警告。然后像这样遍历地图:

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

如果遇到意外的类型,你会得到一个运行时ClassCastException,但至少它会在接近问题根源的地方发生。

【讨论】:

  • 比skiphoppy提供的答案要好得多,原因有很多:1)这段代码要短得多。 2) 此代码实际上按预期抛出 ClassCastException。 3)此代码不做源映射的完整副本。 4) 循环可以很容易地包装在断言中使用的单独方法中,这很容易消除生产代码中的性能损失。
  • Java 编译器或 JIT 编译器是否有可能决定不使用这段代码的结果并通过不编译来“优化”它?
  • 如果它可能引发异常,它并不是真正的死代码。我对今天使用的 JIT 编译器了解得不够多,无法保证它们都不会把事情搞砸,但我有信心说它们不应该这样做。
  • 这仍然不能保证类型安全,因为仍在使用同一个映射。它最初可能被定义为 Map 恰好包含字符串和数字,然后如果添加了布尔值,那么此代码的用户将感到困惑且难以追踪的惊喜。保证类型安全的唯一方法是将其复制到具有请求类型的新映射中,以保证允许进入其中的内容。
【解决方案3】:

哇;我想我找到了自己问题的答案。我只是不确定它是否值得! :)

问题是没有检查演员表。所以,你必须自己检查。您不能只使用 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 之外,我还能做些什么更好的事情?有更好的东西我可以扔吗?我应该将其设为已检查的异常吗?

【讨论】:

  • 这东西令人困惑,但我认为你所做的只是用 ClassCastExceptions 换取 AssertionErrors。
  • 老兄,这绝对不值得!想象一下,可怜的 sap 不得不回来修改一些代码,里面乱七八糟。我不喜欢压制警告,但我认为这是较小的邪恶。
  • 这不仅仅是因为它是一个丑陋的、令人困惑的、混乱的(当你无法避免一个丰富的 cmets 可以引导维护程序员完成它时);遍历集合中的每个元素会将强制转换从 O(1) 转换为 O(n) 操作。这是无法预料的事情,很容易变成可怕的神秘减速。
  • @DanNeely 你是对的。一般来说,没有人应该这样做。
  • 一些 cmets...方法签名是错误的,因为它没有“投射”该死的东西,它只是将现有地图复制到新地图中。此外,它可能会被重构以接受任何映射,而不依赖于 HashMap 本身(即在方法签名中获取 Map 并返回 Map,即使内部类型是 HashMap)。您实际上并不需要将转换或存储到新地图中 - 如果您不抛出断言错误,那么现在给定的地图在其中具有正确的类型。使用泛型类型创建新地图毫无意义,因为您仍然可以将其设为原始并放置任何内容。
【解决方案4】:

在 Eclipse Preferences 中,转到 Java->Compiler->Errors/Warnings->Generic types 并选中 Ignore unavoidable generic type problems 复选框。

这满足了问题的意图,即

我想避免 Eclipse 警告...

如果不是精神。

【讨论】:

  • 啊,谢谢 :) 我在 javac 中收到“uses unchecked or unsafe operations.”错误,但添加 @SuppressWarnings("unchecked") 让 Eclipse 不高兴,声称抑制是不必要的。 取消选中此框使 Eclipse 和 javac 行为相同,这正是我想要的。在代码中显式禁止警告比在 Eclipse 中到处禁止警告要清楚得多。
【解决方案5】:

您可以创建一个如下所示的实用程序类,并使用它来抑制未经检查的警告。

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

【讨论】:

  • 不反对,但包装器除了抑制警告之外没有添加任何内容。
  • +1 因为这个解决方案不会浪费宝贵的代码行。
  • @ErikE 太多了。更昂贵的更大和更高分辨率的显示器可以为所有这些浪费的线路腾出空间,更大的桌子可以放置所有更大的显示器,更大的房间可以放置更大的桌子,以及有见地的老板..
  • @ErikE 滚动条,对于 vi? Are you kidding?
【解决方案6】:

这东西很难,但这是我目前的想法:

如果您的 API 返回 Object,那么您将无能为力——无论如何,您将盲目地转换对象。您让 Java 抛出 ClassCastExceptions,或者您可以自己检查每个元素并抛出 Assertions 或 IllegalArgumentExceptions 或类似的,但这些 runtime 检查都是等效的。无论您在运行时做什么,都必须禁止 编译时间 未经检查的强制转换。

我只是更喜欢盲投并让 JVM 为我执行其运行时检查,因为我们“知道”API 应该返回什么,并且通常愿意假设 API 有效。如果需要,请在演员表上方的任何地方使用泛型。由于您仍然有单个盲投,因此您并没有真正购买任何东西,但至少您可以从那里开始使用泛型,以便 JVM 可以帮助您避免在其他代码段中进行盲投。

在这种特殊情况下,大概您可以看到对 SetAttribute 的调用并看到类型正在输入,因此在输出时将类型盲转换为相同并不是不道德的。添加引用 SetAttribute 的注释并完成它。

【讨论】:

    【解决方案7】:

    这是一个简短的示例,通过采用其他答案中提到的两种策略来避免“未经检查的强制转换”警告

    1. 在运行时将感兴趣类型的类作为参数传递 (Class&lt;T&gt; inputElementClazz)。然后你可以使用:inputElementClazz.cast(anyObject);

    2. 对于集合的类型转换,使用通配符 ?而不是泛型类型 T 来确认您确实不知道从遗留代码中期望什么样的对象 (Collection&lt;?&gt; unknownTypeCollection)。毕竟,这就是“未经检查的演员表”警告想要告诉我们的:我们不能确定我们得到了Collection&lt;T&gt;,所以诚实的做法是使用Collection&lt;?&gt;。如果绝对需要,仍然可以构建已知类型的集合 (Collection&lt;T&gt; knownTypeCollection)。

    下面示例中接口的遗留代码在 StructuredViewer 中有一个属性“input”(StructuredViewer 是一个树或表格小部件,“input”是它背后的数据模型)。这个“输入”可以是任何类型的 Java 集合。

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

    【讨论】:

      【解决方案8】:

      在 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&lt;String,String&gt;,所有内容都将在您编写代码的确切位置进行检查。以后不出意外ClassCastExceptions

      【讨论】:

      • 这个答案真的很有帮助。
      【解决方案9】:

      在 Android Studio 中,如果你想禁用检查,你可以使用:

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

      【讨论】:

        【解决方案10】:

        在这种特殊情况下,我不会将 Maps 直接存储到 HttpSession 中,而是我自己的类的一个实例,该实例又包含一个 Map(该类的实现细节)。然后你可以确定地图中的元素是正确的类型。

        但是如果你还是想检查 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 可能是最好的方法(易于理解和维护),通过运行时检查安全地完成它
        【解决方案11】:

        Esko Luontola 上述答案中的 Objects.Unchecked 实用程序函数是避免程序混乱的好方法。

        如果您不想在整个方法上使用 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
        

        编译器告诉您的是,在运行时不会检查此转换,因此在您尝试访问通用容器中的数据之前不会引发运行时错误。

        【讨论】:

          【解决方案12】:

          警告抑制不是解决方案。您不应该在一个语句中进行两级转换。

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

          【讨论】:

          • 他五年前的问题?你需要做那么多工作吗?鉴于 Java 具有类型擦除,第二个 hashmap 在运行时应该与第一个相同;如果您只是遍历条目并验证它们都是字符串的实例,我认为它会更有效并避免复制。或者,TBH,检查您正在使用的 servlet JAR 的源并验证它是否只放置字符串。
          • 直到今天我在项目中看到了这个警告。他的问题不是类型的验证,而是由“放入”未投射的地图引起的警告。
          【解决方案13】:

          如果你发布你的代码,快速猜测一下可以肯定地说,但你可能已经做了一些类似的事情

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

          当你需要做的时候会产生警告

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

          可能值得一看

          Generics in the Java Programming Language

          如果您不熟悉需要做什么。

          【讨论】:

          • 不幸的是,情况并不那么容易。添加了代码。
          • 我来这里是为了寻找一个稍微不同的问题的答案:你告诉我我需要什么!谢谢!
          【解决方案14】:

          我可能误解了这个问题(一个例子和几行周围的行会很好),但是为什么不总是使用适当的接口(和 Java5+)?我看不出您为什么要转换为HashMap 而不是Map&lt;KeyType,ValueType&gt;。事实上,我无法想象 任何 理由将变量的类型设置为 HashMap 而不是 Map

          为什么来源是Object?它是遗留集合的参数类型吗?如果是这样,请使用泛型并指定所需的类型。

          【讨论】:

          • 我很确定在这种情况下切换到 Map 不会改变任何东西,但是感谢编程技巧,这可能会改变我做某些事情的方式,变得更好。对象的来源是我无法控制的 API(添加了代码)。
          【解决方案15】:

          如果我必须使用不支持泛型的 API。我会尝试用尽可能少的行将这些调用隔离在包装例程中。然后我使用 SuppressWarnings 注释并同时添加类型安全强制转换。

          这只是个人喜好,希望尽可能保持整洁。

          【讨论】:

            【解决方案16】:

            使用这个,它比创建一个新的 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())
            【解决方案17】:

            在投射之前只需进行类型检查。

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

            对于任何询问的人来说,收到您不确定类型的对象是很常见的。大量遗留的“SOA”实现传递了您不应该总是信任的各种对象。 (恐怖!)

            EDIT 更改了一次示例代码以匹配发布者的更新,在一些 cmets 之后,我发现 instanceof 不能很好地与泛型配合使用。但是,更改检查以验证外部对象似乎与命令行编译器配合得很好。修改后的示例现已发布。

            【讨论】:

            • 不幸的是,泛型使这成为不可能。它不仅仅是一个 HashMap,它是一个带有类型信息的 HashMap。如果我消除这些信息,我只会将警告推送到其他地方。
            【解决方案18】:

            几乎计算机科学中的每个问题都可以通过添加间接级别*或其他方式来解决。

            所以引入一个比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();
            }
            

            *除了太多的间接层。

            【讨论】:

            【解决方案19】:

            这是我在覆盖 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 编译)

            【讨论】:

              【解决方案20】:

              如果您确定 session.getAttribute() 返回的类型是 HashMap,那么您不能强制转换为该确切类型,而只能检查通用 HashMap

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

              Eclipse 随后会发出意外警告,但这当然会导致难以调试的运行时错误。我只在非操作关键的上下文中使用这种方法。

              【讨论】:

                【解决方案21】:

                两种方法,一种完全避免标签,另一种使用顽皮但很好的实用方法。
                问题是预泛型集合...
                我相信经验法则是:“一次投射一件事” - 这意味着在泛化世界中尝试使用原始类时,因为您不知道此 Map, ?> (事实上,JVM 甚至可能会发现它甚至不是 Map!),当您考虑它时,很明显您无法转换它。如果你有一个 Map map2 那么 HashSet keys = (HashSet)map2.keySet() 不会给你一个警告,尽管这对编译器来说是一种“信仰行为”(因为它可能会变成一个 TreeSet)...但它只是一个单一的信仰行为。

                PS反对以我的第一种方式进行迭代“很无聊”并且“需要时间”,答案是“没有痛苦就没有收获”:保证泛化集合包含 Map.Entrys,什么都没有别的。您必须为此担保付费。当系统地使用泛型时,这种支付方式非常漂亮,采用编码合规性的形式,而不是机器时间!
                一种思想流派可能会说,您应该设置 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;
                    }
                
                
                }
                

                【讨论】:

                • 您没有评论权限这一事实不允许您编辑其他人的答案以添加您的 cmets;您编辑其他人的答案以在格式、语法等方面改进它们,而不是添加您对它们的意见。当您达到 50 名代表时,您将能够在任何地方发表评论,同时我很确定您可以抗拒(或者,如果您真的不能,请将您的 cmets 写到您帖子中的现有答案)。 (其他人的注意事项:我写这篇文章是因为我看到 - 并拒绝了 - 他提议的 cmets 对审核工具中其他帖子的编辑)
                【解决方案22】:

                这会使警告消失...

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

                【讨论】:

                • 不,它没有。实际上,这会在第一个警告的地方产生两个警告。
                • 啊,好的。不知道为什么我会这么想。
                【解决方案23】:

                解决方案:在 Eclipse 中禁用此警告。不要@SuppressWarnings 它,完全禁用它。

                上面介绍的几个“解决方案”是不合时宜的,为了抑制愚蠢的警告而使代码不可读。

                【讨论】:

                • 请问为什么?全局禁用警告将隐藏存在此问题的其他地方。添加@SuppressWarnings 不会使代码完全不可读。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-03-04
                • 1970-01-01
                • 2012-03-18
                • 2018-10-10
                • 1970-01-01
                相关资源
                最近更新 更多