使用Java泛型键入强制

时间:2018-06-09 19:35:40

标签: java generics

这里我有以下代码:

public final class Generics {
    private Generics() {}

    public static <T> T unchecked(Object obj) {
        return (T)obj; //unchecked cast warning
    }

    public static void main(String[] args) {
        //correct
        ArrayList<String> str = new ArrayList<String>();
        str.add("Hello");
        str.add("World");
        System.out.println(str);

        //"correct" - not type safe at all but due to type erasure this is almost legal
        ArrayList<Object> obj = Generics.<ArrayList<Object>>unchecked(str);
        obj.add(1);
        System.out.println(obj);

        //obviously wrong but still compiles (ClassCastException at runtime)
        Exception except = Generics.<Exception>unchecked(str);
        System.out.println(except);
    }
}

通常情况下,ArrayList<Object> obj = (ArrayList<Object>)str形式的强制转换将是致命的编译错误,因为这会违反使用该列表的期望。 (您可以将不是字符串的对象插入到仅字符串列表中)

什么是&#34;未经检查&#34;在这种情况下警告,unchecked()方法的使用与直接转换引用有何不同?这如何绕过编译器的类型检查?

2 个答案:

答案 0 :(得分:4)

  

什么是&#34;未经检查&#34;在这种情况下警告

未经检查的强制转换只是强制转换,实际上不会在字节码中生成checkcast指令。

public static <T> T unchecked(Object obj) {
    return (T)obj; //unchecked cast warning
}

在这里,它是一个未经检查的演员,因为T的删除是Object,所以它实际上是无操作,因为obj已经是Object

但有害的是, 在呼叫网站上插入了一个经过检查的演员:

Exception except = Generics.<Exception>unchecked(str);
               // ^ cast here!

擦除后,有效执行的代码为:

Exception except = (Exception) Generics.unchecked(str);

并且ClassCastException失败了。但是你没有得到警告,因为方法的返回类型表明这是安全的,因此编译器相信它在呼叫站点是安全的。

调用网站无法知道unchecked方法正在做一些危险的事情。事实上,唯一能做到的是安全的就是返回null。但是编译器并没有考虑它是否正在这样做:它只是采用unchecked方法的声明面值。

  

如何使用unchecked()方法与直接转换引用有什么不同?

不是,真的。唯一的区别是您通过Object隐式投射。所以虽然这不会编译:

Exception except = (Exception) str;

这样做:

Exception except = (Exception) (Object) str;

并且在编译时将使用相同的ClassCastException失败。

答案 1 :(得分:1)

  

通常情况下,ArrayList<Object> obj = (ArrayList<Object>)str形式的强制转换将是致命的编译错误,因为这会违反使用该列表的期望。

该强制转换是一个编译错误,因为ArrayList<String>不是ArrayList<Object>的子类型 - 这与将Double转换为String时获得的编译错误相同

您实际上可以通过使用

来避免使用unchecked方法
  // ArrayList<String> to ArrayList (raw) is legal
  ArrayList<Object> obj = (ArrayList<Object>)(ArrayList) str;
  // you can also cast to an interface-
  // at runtime, str could as well extend that interface!
  // and then Runnable can be downcasted to Exception
  // obviousy wrong, but still legal
  Exception except = (Exception)(Runnable)(str);

转换为非Runnable超类型的接口(ArrayList)是合法的,因为我们很可能有一个扩展ArrayList并实现Runnable的类。 (虽然这会是一个奇怪的课......)

这种类型的转换是&#34;缩小参考转换&#34;,如JLS, 5.1.6中所定义