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

Unchecked cast behavior with and without assignment to variable

为什么 main 中的第一行不抛出 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");
    }
}

泛型只是编译时检查(阅读 type erasure)。所以在运行时你的 getSomething() 方法看起来类似于:

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

现在你看清楚了,第一行永远不会抛出异常

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

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

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

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

A Function 仍然是一个 Object,所以它可以从方法返回,但它不是 String,因此,你得到你的 ClassCastException

代码编译良好,因为您指示编译器您知道自己在做什么。您将在这一行收到 Unchecked cast 警告:

 return (T) fun;

这个警告应该是编译器向你(程序员)发出的一个指示,它(编译器)不能确定转换是否成功。

不同之处在于,第一种情况下您不使用该方法的结果,而第二种情况下您使用该方法的结果。

强制转换是 表达式,但不是 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() 方法,因此它可以在没有转换的情况下调用它。