简体   繁体   English

如何解决未经检查的强制转换警告?

[英]How do I address unchecked cast warnings?

Eclipse is giving me a warning of the following form: Eclipse 给我以下形式的警告:

Type safety: Unchecked cast from Object to HashMap类型安全:未经检查从 Object 到 HashMap

This is from a call to an API that I have no control over which returns Object:这是对 API 的调用,我无法控制返回 Object:

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

I'd like to avoid Eclipse warnings, if possible, since theoretically they indicate at least a potential code problem.如果可能的话,我想避免 Eclipse 警告,因为理论上它们至少表明存在潜在的代码问题。 I haven't found a good way to eliminate this one yet, though.不过,我还没有找到消除这个问题的好方法。 I can extract the single line involved out to a method by itself and add @SuppressWarnings("unchecked") to that method, thus limiting the impact of having a block of code where I ignore warnings.我可以将涉及的单行单独提取到一个方法中,并将@SuppressWarnings("unchecked")添加到该方法中,从而限制了我忽略警告的代码块的影响。 Any better options?有更好的选择吗? I don't want to turn these warnings off in Eclipse.我不想在 Eclipse 中关闭这些警告。

Before I came to the code, it was simpler, but still provoked warnings:在我来到代码之前,它更简单,但仍然引发了警告:

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

Problem was elsewhere when you tried to use the hash you'd get warnings:当您尝试使用 hash 时,问题出在其他地方,您会收到警告:

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.

The obvious answer, of course, is not to do the unchecked cast.当然,显而易见的答案是不要进行未经检查的演员表。

If it's absolutely necessary, then at least try to limit the scope of the @SuppressWarnings annotation.如果绝对有必要,那么至少尝试限制@SuppressWarnings注释的范围。 According to its Javadocs , it can go on local variables;根据它的Javadocs ,它可以继续使用局部变量; this way, it doesn't even affect the entire method.这样,它甚至不会影响整个方法。

Example:例子:

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

There is no way to determine whether the Map really should have the generic parameters <String, String> .无法确定Map是否真的应该具有通用参数<String, String> You must know beforehand what the parameters should be (or you'll find out when you get a ClassCastException ).您必须事先知道参数应该是什么(否则您会在收到ClassCastException时发现)。 This is why the code generates a warning, because the compiler can't possibly know whether is safe.这就是代码生成警告的原因,因为编译器不可能知道是否安全。

Unfortunately, there are no great options here.不幸的是,这里没有很好的选择。 Remember, the goal of all of this is to preserve type safety.请记住,所有这些的目标是保持类型安全。 " Java Generics " offers a solution for dealing with non-genericized legacy libraries, and there is one in particular called the "empty loop technique" in section 8.2. Java 泛型”提供了一种处理非泛型遗留库的解决方案,在 8.2 节中有一种特别称为“空循环技术”的方法。 Basically, make the unsafe cast, and suppress the warning.基本上,进行不安全的转换,并抑制警告。 Then loop through the map like this:然后像这样循环遍历地图:

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

If an unexpected type is encountered, you will get a runtime ClassCastException , but at least it will happen close to the source of the problem.如果遇到意外类型,您将获得运行时ClassCastException ,但至少它会在靠近问题根源的地方发生。

Wow;哇; I think I figured out the answer to my own question.我想我找到了自己问题的答案。 I'm just not sure it's worth it!我只是不确定它是否值得! :) :)

The problem is the cast isn't checked.问题是没有检查演员表。 So, you have to check it yourself.所以,你必须自己检查。 You can't just check a parameterized type with instanceof, because the parameterized type information is unavailable at runtime, having been erased at compile time.您不能只使用 instanceof 检查参数化类型,因为参数化类型信息在运行时不可用,在编译时已被删除。

But, you can perform a check on each and every item in the hash, with instanceof, and in doing so, you can construct a new hash that is type-safe.但是,您可以使用 instanceof 对散列中的每一项进行检查,这样您就可以构造一个类型安全的新散列。 And you won't provoke any warnings.而且你不会引起任何警告。

Thanks to mmyers and Esko Luontola, I've parameterized the code I originally wrote here, so it can be wrapped up in a utility class somewhere and used for any parameterized HashMap.感谢 mmyers 和 Esko Luontola,我已经参数化了我最初在这里编写的代码,因此可以将其封装在某个实用程序类中并用于任何参数化的 HashMap。 If you want to understand it better and aren't very familiar with generics, I encourage viewing the edit history of this answer.如果您想更好地理解它并且对泛型不是很熟悉,我鼓励您查看此答案的编辑历史记录。

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

That's a lot of work, possibly for very little reward... I'm not sure if I'll use it or not.这是很多工作,可能回报很少......我不确定我是否会使用它。 I'd appreciate any comments as to whether people think it's worth it or not.对于人们是否认为值得或不值得的任何评论,我将不胜感激。 Also, I'd appreciate improvement suggestions: is there something better I can do besides throw AssertionErrors?另外,我很感激改进建议:除了抛出 AssertionErrors 之外,我还能做些什么更好的事情吗? Is there something better I could throw?有什么更好的东西我可以扔吗? Should I make it a checked Exception?我应该让它成为一个检查异常吗?

In Eclipse Preferences, Go to Java->Compiler->Errors/Warnings->Generic types and check the Ignore unavoidable generic type problems check-box.在 Eclipse Preferences 中,转到 Java->Compiler->Errors/Warnings->Generic types 并选中Ignore unavoidable generic type problems复选框。

This satisfies the intent of the question, ie这满足问题的意图,即

I'd like to avoid Eclipse warnings...我想避免 Eclipse 警告...

if not the spirit.如果没有精神。

You can create a utility class like the following, and use it to suppress the unchecked warning.您可以创建如下所示的实用程序类,并使用它来取消未选中的警告。

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

You can use it as follows:您可以按如下方式使用它:

import static Objects.uncheckedCast;
...

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

Some more discussion about this is here: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html关于这个的更多讨论在这里: http : //cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html

This stuff is hard, but here are my current thoughts:这东西很难,但这是我目前的想法:

If your API returns Object, then there's nothing you can do -- no matter what, you will be blindly casting the object.如果您的 API 返回对象,那么您无能为力——无论如何,您都将盲目地投射对象。 You let Java throw ClassCastExceptions, or you can check each element yourself and throw Assertions or IllegalArgumentExceptions or some such, but these runtime checks are all equivalent.您让 Java 抛出 ClassCastExceptions,或者您可以自己检查每个元素并抛出 Assertions 或 IllegalArgumentExceptions 或类似的东西,但这些运行时检查都是等效的。 You have to suppress the compile time unchecked cast no matter what you do at runtime.无论您在运行时做什么,您都必须禁止编译时未经检查的强制转换。

I'd just prefer to blind cast and let the JVM perform its runtime check for me since we "know" what the API should return, and are usually willing to assume that the API works.我只是更喜欢盲目转换并让 JVM 为我执行其运行时检查,因为我们“知道”API 应该返回什么,并且通常愿意假设 API 有效。 Use generics everywhere above the cast, if you need them.如果需要,请在演员表上方的任何地方使用泛型。 You aren't really buying anything there since you still have the single blind cast, but at least you can use generics from there on up so the JVM can help you avoid blind casts in other pieces of your code.您实际上并没有在那里购买任何东西,因为您仍然有一个盲目的强制转换,但至少您可以从那里开始使用泛型,这样 JVM 可以帮助您避免在其他代码段中进行盲目的强制转换。

In this particular case, presumably you can see the call to SetAttribute and see the type is going in, so just blind-casting the type to same on the way out is not immoral.在这种特殊情况下,大概您可以看到对 SetAttribute 的调用并看到类型正在进入,因此在退出时盲目地将类型转换为相同的类型并不是不道德的。 Add a comment referencing the SetAttribute and be done with it.添加引用 SetAttribute 的注释并完成它。

Here is a shortened example that avoids the "unchecked cast" warning by employing two strategies mentioned in other answers.这是一个简短的示例,它通过采用其他答案中提到的两种策略来避免“未经检查的强制转换”警告

  1. Pass down the Class of the type of interest as a parameter at runtime ( Class<T> inputElementClazz ).在运行时将感兴趣类型的 Class 作为参数传递 ( Class<T> inputElementClazz )。 Then you can use: inputElementClazz.cast(anyObject);然后你可以使用: inputElementClazz.cast(anyObject);

  2. For type casting of a Collection, use the wildcard ?对于集合的类型转换,请使用通配符 ? instead of a generic type T to acknowledge that you indeed do not know what kind of objects to expect from the legacy code ( Collection<?> unknownTypeCollection ).而不是泛型类型 T 来承认您确实不知道遗留代码( Collection<?> unknownTypeCollection )期望什么样的对象。 After all, this is what the "unchecked cast" warning wants to tell us: We cannot be sure that we get a Collection<T> , so the honest thing to do is to use a Collection<?> .毕竟,这就是“unchecked cast”警告想要告诉我们的:我们不能确定我们得到的是Collection<T> ,所以诚实的做法是使用Collection<?> If absolutely needed, a collection of a known type can still be built ( Collection<T> knownTypeCollection ).如果绝对需要,仍然可以构建已知类型的集合( Collection<T> knownTypeCollection )。

The legacy code interfaced in the example below has an attribute "input" in the StructuredViewer (StructuredViewer is a tree or table widget, "input" is the data model behind it).下面示例中接口的遗留代码在 StructuredViewer 中有一个属性“input”(StructuredViewer 是一个树或表格小部件,“input”是它背后的数据模型)。 This "input" could be any kind of Java Collection.这个“输入”可以是任何类型的 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();
}

Naturally, the code above can give runtime errors if we use the legacy code with the wrong data types (eg if we set an array as the "input" of the StructuredViewer instead of a Java Collection).自然地,如果我们使用具有错误数据类型的遗留代码(例如,如果我们将数组设置为 StructuredViewer 而不是 Java 集合的“输入”),上面的代码可能会出现运行时错误。

Example of calling the method:调用方法示例:

dragFinishedStrategy.dragFinished(viewer, Product.class);

In the HTTP Session world you can't really avoid the cast, since the API is written that way (takes and returns only Object ).在 HTTP Session 世界中,您无法真正避免强制转换,因为 API 是以这种方式编写的(仅接受并返回Object )。

With a little bit of work you can easily avoid the unchecked cast, 'though.不过,只要稍加努力,您就可以轻松避免未经检查的演员表。 This means that it will turn into a traditional cast giving a ClassCastException right there in the event of an error).这意味着它将变成传统的ClassCastException ,在发生错误时立即给出ClassCastException )。 An unchecked exception could turn into a CCE at any point later on instead of the point of the cast (that's the reason why it's a separate warning).未经检查的异常可能会在以后的任何时候变成CCE ,而不是在转换的时候(这就是为什么它是一个单独的警告的原因)。

Replace the HashMap with a dedicated class:用专用类替换 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);
    }
}

Then cast to that class instead of Map<String,String> and everything will be checked at the exact place where you write your code.然后转换为该类而不是Map<String,String>并且所有内容都将在您编写代码的确切位置进行检查。 No unexpected ClassCastExceptions later on.以后没有意外的ClassCastExceptions

In Android Studio if you want to disable inspection you can use:在 Android Studio 中,如果您想禁用检查,您可以使用:

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

In this particular case, I would not store Maps into the HttpSession directly, but instead an instance of my own class, which in turn contains a Map (an implementation detail of the class).在这种特殊情况下,我不会直接将 Map 存储到 HttpSession 中,而是将我自己的类的一个实例存储到其中,该实例又包含一个 Map(类的实现细节)。 Then you can be sure that the elements in the map are of the right type.然后您可以确定地图中的元素是正确的类型。

But if you anyways want to check that the contents of the Map are of right type, you could use a code like this:但是,如果您无论如何想要检查 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);
    }
}

The Objects.Unchecked utility function in the answer above by Esko Luontola is a great way to avoid program clutter.上面 Esko Luontola 的回答中的 Objects.Unchecked 实用程序函数是避免程序混乱的好方法。

If you don't want the SuppressWarnings on an entire method, Java forces you to put it on a local.如果您不想在整个方法上使用 SuppressWarnings,Java 会强制您将其放在本地。 If you need a cast on a member it can lead to code like this:如果您需要对成员进行强制转换,则可能会导致如下代码:

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

Using the utility is much cleaner, and it's still obvious what you are doing:使用该实用程序要干净得多,而且您在做什么仍然很明显:

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

NOTE: I feel its important to add that sometimes the warning really means you are doing something wrong like :注意:我觉得补充一点很重要,有时警告真的意味着你做错了,比如:

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

What the compiler is telling you is that this cast will NOT be checked at runtime, so no runtime error will be raised until you try to access the data in the generic container.编译器告诉您的是,不会在运行时检查此转换,因此在您尝试访问通用容器中的数据之前不会引发运行时错误。

Warning suppression is not a solution.警告抑制不是解决方案。 You should not be doing two level casting in one statement.您不应该在一个语句中进行两级转换。

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

Take this one, it's much faster than creating a new HashMap, if it's already one, but still secure, as each element is checked against it's type...拿这个来说,它比创建一个新的 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);
    }

A quick guess if you post your code can say for sure but you might have done something along the lines of快速猜测您是否发布了代码可以肯定地说,但您可能已经做了一些类似的事情

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

which will produce the warning when you need to do当你需要做的时候会产生警告

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

it might be worth looking at可能值得一看

Generics in the Java Programming Language Java 编程语言中的泛型

if your unfamiliar with what needs to be done.如果您不熟悉需要做什么。

I may have misunderstood the question(an example and a couple of surrounding lines would be nice), but why don't you always use an appropriate interface (and Java5+)?我可能误解了这个问题(一个例子和几行周围的行会很好),但你为什么不总是使用适当的界面(和 Java5+)? I see no reason why you would ever want to cast to a HashMap instead of a Map<KeyType,ValueType> .我看不出您为什么要转换为HashMap而不是Map<KeyType,ValueType> In fact, I can't imagine any reason to set the type of a variable to HashMap instead of Map .事实上,我无法想象有任何理由将变量的类型设置为HashMap而不是Map

And why is the source an Object ?为什么源是Object Is it a parameter type of a legacy collection?它是遗留集合的参数类型吗? If so, use generics and specify the type you want.如果是这样,请使用泛型并指定所需的类型。

If I have to use an API that doesn't support Generics.. I try and isolate those calls in wrapper routines with as few lines as possible.如果我必须使用不支持泛型的 API.. 我尝试用尽可能少的行隔离包装例程中的这些调用。 I then use the SuppressWarnings annotation and also add the type-safety casts at the same time.然后我使用 SuppressWarnings 注释并同时添加类型安全强制转换。

This is just a personal preference to keep things as neat as possible.这只是个人喜好,以保持尽可能整洁。

Here's one way I handle this when I override the equals() operation.这是我在覆盖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);
    }
}

This seems to work in Java 8 (even compiled with -Xlint:unchecked )这似乎适用于 Java 8(甚至使用-Xlint:unchecked编译)

Just typecheck it before you cast it.只需在投射之前打字检查即可。

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

And for anyone asking, it's quite common to receive objects where you aren't sure of the type.对于任何询问的人来说,收到不确定类型的对象是很常见的。 Plenty of legacy "SOA" implementations pass around various objects that you shouldn't always trust.许多遗留的“SOA”实现会传递您不应该总是信任的各种对象。 (The horrors!) (恐怖!)

EDIT Changed the example code once to match the poster's updates, and following some comments I see that instanceof doesn't play nicely with generics.编辑更改了一次示例代码以匹配海报的更新,根据一些评论,我发现 instanceof 与泛型不能很好地配合。 However changing the check to validate the outer object seems to play well with the commandline compiler.然而,更改检查以验证外部对象似乎与命令行编译器配合得很好。 Revised example now posted.修订后的示例现已发布。

Almost every problem in Computer Science can be solved by adding a level of indirection*, or something.几乎计算机科学中的每个问题都可以通过添加一个间接级别* 或其他东西来解决。

So introduce a non-generic object that is of a higher-level that a Map .因此,引入一个比Map更高级别的非通用对象。 With no context it isn't going to look very convincing, but anyway:没有上下文,它看起来不会很有说服力,但无论如何:

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 too many levels of indirection. *除了太多的间接级别。

If you are sure that the type returned by session.getAttribute() is HashMap then you can not typecast to that exact type, but rely on only checking the generic HashMap如果您确定 session.getAttribute() 返回的类型是 HashMap,那么您不能将类型转换为该确切类型,而只能依靠检查通用 HashMap

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

Eclipse will then surprise warnings, but of course this can lead to runtime errors that can be hard to debug. Eclipse 随后会发出意外警告,但这当然会导致难以调试的运行时错误。 I use this approach in not operation-critical contexts only.我仅在非操作关键的上下文中使用这种方法。

Two ways, one which avoids the tag completely, the other using a naughty but nice utility method.两种方法,一种完全避免标签,另一种使用顽皮但很好的实用方法。
The problem is pre-genericised Collections...问题是预先通用的集合......
I believe the rule of thumb is: "cast objects one thing at a time" - what this means when trying to use raw classes in a genericised world is that because you don't know what is in this Map<?, ?> (and indeed the JVM might even find that it isn't even a Map!), it obvious when you think about it that you can't cast it.我相信经验法则是:“一次只投射一个对象”——这意味着在泛型世界中尝试使用原始类时,因为你不知道这个 Map<?, ?> (事实上,JVM 甚至可能会发现它甚至不是 Map!),当你​​想到它时,很明显你不能投射它。 If you had a Map<String, ?> map2 then HashSet<String> keys = (HashSet<String>)map2.keySet() does not give you a warning, despite this being an "act of faith" for the compiler (because it might turn out to be a TreeSet)... but it is only a single act of faith.如果你有一个 Map<String, ?> map2 那么 HashSet<String> keys = (HashSet<String>)map2.keySet() 不会给你一个警告,尽管这是编译器的“信仰行为”(因为它可能会变成一个 TreeSet)......但这只是一个单一的信仰行为。

PS to the objection that iterating as in my first way "is boring" and "takes time", the answer is "no pain no gain": a genericised collection is guaranteed to contain Map.Entry<String, String>s, and nothing else. PS 反对以我的第一种方式进行迭代“很无聊”和“需要时间”,答案是“没有痛苦就没有收获”:泛型集合保证包含 Map.Entry<String, String> s,什么都没有别的。 You have to pay for this guarantee.您必须为此担保付费。 When using generics systematically this payment, beautifully, takes the form of coding compliance, not machine time!当系统地使用泛型时,这种付款以编码合规的形式出现,而不是机器时间!
One school of thought might say that you should set Eclipse's settings to make such unchecked casts errors, rather than warnings.一种思想流派可能会说,您应该设置 Eclipse 的设置以生成此类未经检查的强制转换错误,而不是警告。 In that case you would have to use my first way.在这种情况下,您将不得不使用我的第一种方式。

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


}
HashMap<String, String> theHash = ((Map<?,?>) session.getAttribute("attributeKey")).entrySet().stream().collect(Collectors.toMap(entry -> (String) entry.getKey(), entry -> (String) entry.getValue()));

This makes the warnings go away...这使得警告消失......

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

Solution: Disable this warning in Eclipse.解决方案:在 Eclipse 中禁用此警告。 Don't @SuppressWarnings it, just disable it completely.不要@SuppressWarnings 它,只需完全禁用它。

Several of the "solutions" presented above are way out of line, making code unreadable for the sake of suppressing a silly warning.上面提出的几个“解决方案”是不合时宜的,为了抑制愚蠢的警告而使代码不可读。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM