没有 ClassCast强制转换为与实际类不同的泛型类型时的异常

2022-09-04 06:06:08

我有一些代码看起来像这样(该方法的负面测试的一部分):get

import java.util.*;
public class Test {
    Map<String, Object> map = new HashMap<>();
    public static void main (String ... args) {
        Test test = new Test();
        test.put("test", "value"); // Store a String
        System.out.println("Test: " + test.get("test", Double.class)); // Retrieve it as a Double
    }

    public <T> T get(String key, Class<T> clazz) {
        return (T) map.get(key);
    }

    public void put(String key, Object value) {
        map.put(key, value);
    }
}

我本来以为它会抛出一个,但它运行成功打印:ClassCastException

Test: value

为什么它不扔?


答案 1

这是因为您正在强制转换为泛型类型 ,该类型在运行时被擦除,就像所有Java泛型一样。因此,在运行时实际发生的情况是,您正在强制转换为 而不是 。TObjectDouble

请注意,例如,如果定义为 ,您将强制转换为 (但仍然不强制转换为 )。T<T extends Number>NumberDouble

如果要执行一些运行时类型检查,则需要使用实际参数(在运行时可用),而不是泛型类型(已擦除)。例如,您可以执行如下操作:clazzT

public <T> T get(String key, Class<T> clazz) {
    return clazz.cast(map.get(key));
}

答案 2

我发现在返回的“Double”上调用方法时,字节码存在差异,并且不调用任何方法。

例如,如果您要在返回的“Double”上调用(甚至),则会发生。使用,我得到以下字节码:doubleValue()getClass()ClassCastExceptionjavap -c Test

34: ldc           #15                 // class java/lang/Double
36: invokevirtual #16 // Method get (Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
39: checkcast     #15                 // class java/lang/Double
42: invokevirtual #17                 // Method java/lang/Double.doubleValue:()D
45: invokevirtual #18 // Method java/lang/StringBuilder.append:(D)Ljava/lang/StringBuilder;

该操作必须抛出 .此外,在隐式中,会被调用。checkcastClassCastExceptionStringBuilderappend(double)

不调用 (或 ):doubleValue()getClass()

34: ldc           #15                 // class java/lang/Double
36: invokevirtual #16 // Method get:(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
39: invokevirtual #17 // Method java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;

没有操作,并且被调用隐式,因为在类型擦除之后,无论如何都是。checkcastappend(Object)StringBuilderTObject


推荐