带有和不带有变量分配的未经检查的强制转换行为

时间:2019-05-31 12:27:13

标签: java generics unchecked-exception

为什么main中的第一行不引发ClassCastException而第二行却引发ClassCastException?

import java.util.function.Function;

class Scratch {

    static <T> T getSomething(Function<Integer, T> fun) {
        return (T) fun;
    }

    public static void main(String[] args) {
        Scratch.<String>getSomething(x -> "hello");
        String something = Scratch.<String>getSomething(x -> "hello");
    }
}

2 个答案:

答案 0 :(得分:2)

区别在于,在第一种情况下,您不使用方法的结果,而在第二种情况下,您使用了方法的结果。

强制类型转换是表达式,但不是StatementExpression。这意味着您不能这样写:

(String) somethingReturningAString();

但是您可以写:

String aString = (String) somethingReturningAString();

在编译时,编译器将checkcast指令插入需要的位置以及可以插入的位置:

  • 第一种情况无法插入演员表,因此不会进行检查。
  • 在第二种情况下,它可以(必须)插入一个强制类型转换,以确保它向String变量分配实际上是String的内容。因此,它会检查演员表,但失败。

值得注意的是,在某些情况下也许不是强制性强制强制转换,而是强制强制性强制转换已插入。例如:

Scratch.<String>getSomething(x -> "hello").toString();

失败的原因是ClassCastException,因为它将被转换为:

((String) Scratch.getSomething(x -> "hello")).toString();

尽管Object有一个toString()方法,所以它可以在不进行强制转换的情况下调用它。

答案 1 :(得分:1)

泛型仅是编译时检查(有关type erasure的信息)。因此,在运行时,您的getSomething()方法看起来类似于:

static Object getSomething(Function fun) {
    return fun;
}

现在您可以清楚地看到第一行永远不会抛出异常

Scratch.getSomething(x -> "hello");

因为Function Object,因此可以毫无问题地返回。

但是第二行 将抛出第一行,因为它看起来类似于:

String something = (String) Scratch.getSomething(x -> "hello");

Function仍然是Object,因此可以从方法中返回它,但它不是String,因此,您得到了ClassCastException

代码可以很好地编译,因为您可以指示编译器您知道自己在做什么。您会在此行上收到Unchecked cast警告:

 return (T) fun;

此警告应该是编译器向程序员(即编译器)不能确保强制转换成功的指示。