当前位置:  开发笔记 > 编程语言 > 正文

如何处理未经检查的投射警告?

如何解决《如何处理未经检查的投射警告?》经验,为你挑选了12个好方法。

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

类型安全:从Object到HashMap的未选中转换

这是来自对我无法控制的API的调用返回Object:

HashMap getItems(javax.servlet.http.HttpSession session) {
  HashMap theHash = (HashMap)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 should be parameterized.

Michael Myer.. 534

当然,显而易见的答案是不做无节制的演员.

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

例:

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

无法确定是否Map真的应该具有通用参数.你必须事先知道参数应该是什么(或者你得到的时候会发现ClassCastException).这就是代码生成警告的原因,因为编译器不可能知道是否安全.



1> Michael Myer..:

当然,显而易见的答案是不做无节制的演员.

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

例:

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

无法确定是否Map真的应该具有通用参数.你必须事先知道参数应该是什么(或者你得到的时候会发现ClassCastException).这就是代码生成警告的原因,因为编译器不可能知道是否安全.


+1表示它可以继续局部变量.Eclipse只提供将其添加到整个方法中...
警告不仅仅是因为编译器不知道强制转换是安全的.例如`String s =(String)new Object();`没有警告,即使编译器不知道强制转换是安全的.警告是因为编译器(a)不知道强制转换是安全的,并且(b)不会在强制转换点生成完整的运行时检查.将检查它是一个`Hashmap`,但不会检查它是`HashMap `.
Eclipse 3.7(Indigo)支持将unchecked添加到局部变量.
遗憾的是,即使演员和警告是针对*赋值*,注释也必须继续变量声明...所以如果声明和赋值位于不同的地方(比如,在'try'块的外部和内部分别),Eclipse现在生成两个警告:原始未经检查的强制转换,以及一个新的"不必要的注释"诊断.
需要伴随局部变量声明的注释的变通方法(可能在与实际强制转换不同的行中的不同范围内)是在强制转换范围内创建局部变量,专门用于在同一行上执行强制转换作为声明.然后将此变量分配给不同范围内的实际变量.这是我用来抑制对实例变量的强制转换警告的方法,因为这里也不能应用注释.

2> Julien Chast..:

不幸的是,这里没有很好的选择.请记住,所有这一切的目标是保持类型安全." Java Generics "提供了处理非泛化遗留库的解决方案,在8.2节中有一个特别称为"空循环技术".基本上,进行不安全的转换,并禁止警告.然后循环遍历地图,如下所示:

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

如果遇到意外类型,您将获得运行时ClassCastException,但至少它会发生在问题的根源附近.


Java编译器或JIT编译器是否有可能决定不使用此代码的结果并通过不编译来"优化"它?
比skiphoppy提供的答案要好得多,原因有多种:1)这段代码要短得多.2)此代码实际上按预期抛出ClassCastException.3)此代码不执行源映射的完整副本.4)循环可以很容易地用断言中使用的单独方法包装,这样可以轻松地消除生产代码中的性能损失.
这仍然不能保证类型安全,因为仍在使用同一映射。它最初可能被定义为Map <Object,Object>,恰好恰好包含了字符串和数字,然后再添加一个布尔值,则此代码的用户将感到困惑,并且很难追踪到意外。保证类型安全的唯一方法是将其复制到具有保证允许的内容的请求类型的新映射中。

3> skiphoppy..:

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

问题是没有检查演员表.所以,你必须自己检查一下.您不能只使用instanceof检查参数化类型,因为参数化类型信息在运行时不可用,已在编译时擦除.

但是,您可以使用instanceof对哈希中的每个项目执行检查,这样,您就可以构造一个类型安全的新哈希.而且你不会引发任何警告.

感谢mmyers和Esko Luontola,我已经参数化了我最初在这里编写的代码,因此它可以包含在某个实用程序类中并用于任何参数化的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;
}

这是很多工作,可能只是很少的奖励......我不确定我是否会使用它.我很感激有关人们是否认为值得的评论.另外,我很欣赏改进建议:除了抛出AssertionErrors之外,我还能做些什么吗?我能抛出更好的东西吗?我应该将其设为已检查的例外吗?


它不仅仅是一个丑陋,令人困惑的混乱(当你无法避免一个大量的评论可以让维护程序员通过它); 迭代集合中的每个元素将转换从O(1)转换为O(n)操作.这是一个永远不会被期待的东西,很容易变成一个可怕的神秘减速.
这个东西很混乱,但我认为你所做的就是为AssertionErrors交换ClassCastExceptions.
老兄,这绝对不值得!想象一下,那个可怜的傻瓜必须回来修改一些那些混乱的代码.我不喜欢压制警告,但我认为这是较小的邪恶.
@DanNeely你是对的.一般来说,没有人应该这样做.
一些评论......方法签名是错误的,因为它没有"施放"该死的东西,它只是将现有地图复制到新地图中.此外,它可能会被重构以接受任何映射,而不是依赖于HashMap本身(即,即使内部类型是HashMap,也可以使用Map并在方法签名中返回Map).你真的不需要在新地图中进行转换或存储 - 如果你没有抛出断言错误,那么现在给定的地图里面有正确的类型.使用泛型类型创建新地图毫无意义,因为您仍然可以将其生成并放置任何内容.
@skiphoppy我认为你应该更新你的答案,添加一些巨大的警告,说明为什么不应该在这种情况下使用这个(可爱的)泛型代码.我认为我们都同意一个重大的性能损失和维护噩梦在这里不适合抑制构建警告.

4> Dave..:

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

这满足了问题的意图,即

我想避免Eclipse警告......

如果不是精神.



5> Esko Luontol..:

您可以创建如下所示的实用程序类,并使用它来禁止未经检查的警告.

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


不是低估,但是包装器恰好不会仅仅抑制警告.
+1这个解决方案不会浪费宝贵的代码行.

6> Dustin Getz..:

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

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

我只是喜欢盲目转换,让JVM为我执行运行时检查,因为我们"知道"API应该返回什么,并且通常愿意假设API有效.如果需要,可以在演员阵容上方的任何地方使用泛型.你并没有真正购买任何东西,因为你仍然有单盲,但至少你可以从那里使用泛型,所以JVM可以帮助你避免代码中其他部分的盲目转换.

在这种特殊情况下,大概你可以看到对SetAttribute的调用并看到类型正在进入,所以在出路时盲目地将类型转换为相同并不是不道德的.添加引用SetAttribute的注释并完成它.



7> Joachim Saue..:

在HTTP Session世界中,您无法真正避免强制转换,因为API是以这种方式编写的(只接受并返回Object).

但是,通过一些工作,你可以轻松地避免无节制的演员阵容.这意味着它将变成一个传统的演员ClassCastException,在发生错误的情况下在那里给予权利).未经检查的异常可能会CCE在以后的任何时候转变为a 而不是转换点(这就是为什么它是一个单独的警告).

用专用类替换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 {
    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);
    }
}

然后转换到该类而不是,Map并在您编写代码的确切位置检查所有内容.ClassCastExceptions以后没有意外.



8> StaticNoiseL..:

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

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

    对于Collection的类型转换,请使用通配符?而不是通用类型T,以确认您确实不知道遗留代码(Collection unknownTypeCollection)期望什么类型的对象.毕竟,这就是"未经检查的演员"警告要告诉我们的事情:我们不能确定我们得到了一个Collection,所以诚实的做法是使用一个Collection.如果绝对需要,仍然可以构建已知类型的集合(Collection knownTypeCollection).

以下示例中的遗留代码接口在StructuredViewer中具有属性"input"(StructuredViewer是树或表小部件,"input"是其背后的数据模型).这个"输入"可以是任何类型的Java Collection.

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



9> Esko Luontol..:

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

但是如果你想要检查Map的内容是否是正确的类型,你可以使用这样的代码:

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



10> Jan Moravec..:

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

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



11> Gonen I..:

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

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

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



12> abbas..:

警告抑制不是解决方案。您不应该在一项声明中进行两级转换。

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

推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有