本文介绍了泛型奇怪 - 我可以将一个Long值插入到一个Map< String,String>它编译并且不会在运行时失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码:

Give the following code:

public static void main(String[] args) {
        HashMap<String, String> hashMap = new HashMap<>();
        HashMap<String, Object> dataMap = new HashMap<>();
        dataMap.put("longvalue", 5L);

        class TestMethodHolder {
            <T> T getValue(Map<String, Object> dataMap, String value) {
                return (T)dataMap.get(value);
            }
        }

        hashMap.put("test", new TestMethodHolder().<String>getValue(dataMap, "longvalue"));
        String value = hashMap.get("test"); // ClassCastException occurs HERE
        System.out.println(value);
    }

这段代码编译并不奇怪,而是ClassCastException发生在获得线上,而不是上面的投入线,虽然我确实有可能发生什么的教育猜测。由于泛型类型在运行时被擦除,因此getValue()中的强制转换实际上从不会在运行时发生,并且实际上是对Object的强制转换。如果该方法在下面实现,那么运行时间会发生,并且在放置线上会失败(如预期的那样)。任何人都可以证实这一点吗?

It is not surprising to me that this code compiles, but rather that the ClassCastException occurs on the get line as opposed to the put line above it, though I do have an educated guess as to what what may be occurring. Since generic types are erased during runtime, the cast in getValue() actually never occurs at runtime and is effectively a cast to Object. If the method would be implemented below as follows, then the runtime cast would occur and it would fail on the put line (as expected). Can anyone confirm this?

class TestMethodHolder {
        String getValue(Map<String, Object> dataMap, String value) {
            return (String)dataMap.get(value);
        }
    }

这是一个使用泛型的已知缺陷或奇怪吗?在调用方法时使用<>符号是不好的做法?

Is this a known flaw or oddity of using generics? Is it bad practice then to use the <> notation when calling methods?

编辑:
我使用默认的Oracle JDK 1.7_03。

I am using the default Oracle JDK 1.7_03.

上面的另一个隐含的问题是:原始getValue STILL中的转换是否在运行时发生,但转换实际上是转换为Object - 还是编译器足够聪明以将此转换从不发生在运行时呢?这可能解释了人们在运行时出现ClassCastException的地方的差异。

Another implied question from above: Is the cast in the original getValue STILL occurring at runtime but the cast is actually to Object - or is the compiler smart enough to remove this cast from not occurring at runtime at all? This might explain the difference of where the ClassCastException is occurring that people are noticing when running it.

推荐答案

编译器依赖于类型安全性做出假设并做转换/优化。不幸的是,类型安全可以通过未经检查的转换来破坏。如果你的程序包含不正确的未经检查的强制转换,那么编译器应该做什么还不清楚。理想情况下,当 Object 被转换为 T 。但这是不可能的,由于擦除,这不是类型系统的一部分。

Compiler depends on type safety to make assumptions and do transformations/optimizations. Unfortunately the type safety can be subverted through unchecked cast. If your program contains incorrect unchecked cast, it is unclear what the compiler should do. Ideally it should make a runtime check at the exact point of unchecked cast, in your example, when Object is casted to T. But that is impossible, due to erasure, which is not exactly part of the type system.

在你的例子中的其他地方,类型是健全的,所以编译器可以假设 getValue()确实会返回一个 String ,所以不需要重复检查。但是执行检查也是合法的,正如Eclipse编译器一样(可能是因为它将返回值分配给 String 本地temp变量)。

Everywhere else in your example, types are sound, so compiler can assume that getValue() really returns a String, it is unnecessary to double check. But it's also legal to do the check, as Eclipse compiler does (probably because it assigns the return value to a String local temp variable).

所以坏消息是,如果你的程序包含不正确的未经检查的转换,它的行为是未定义的....因此,通过严格的推理,确保所有未经检查的转换都是正确的。

So the bad news is, if your program contains incorrect unchecked cast, its behavior is undefined.... So make sure all your unchecked casts are correct, through rigorous reasoning.

一个好的做法是检查所有未经检查的强制转换,以便合法地禁止未经检查的警告。例如

A good practice is to check all unchecked casts so you can legitimately suppress the unchecked warning. For example

        <T> T getValue(Map<String, Object> dataMap, String value, Class<T> type) 
        { 
            Object value = dataMap.get(value);
            if(value!=null && !type.isInstance(value))  // check!
                throw new ClassCastException();

            @SuppressWarning("unchecked")
            T t = (T)value;  // this is safe, because we've just checked
            return t;
        }

查看我对类似问题的回答:

See my answer to a similar question: Lazy class cast in Java?

这篇关于泛型奇怪 - 我可以将一个Long值插入到一个Map&lt; String,String&gt;它编译并且不会在运行时失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-02 13:00