我错过了 Java 泛型类型的问题
public class MyTest {
@Test
public void test1() throws Exception {
List<Integer> list = getMapInstance();
}
public static <T extends Map<Object, Object>> T getMapInstance() {
T t = (T) new HashMap<>();
return t;
}
}
在代码中,我将一个T(扩展了Map
//the bytecode of test1
0 invokestatic #10 <org/example/BugTest.getMapInstance : ()Ljava/util/Map;>
3 checkcast #11 <java/util/List>
6 astore_1
7 return
我的JDK版本是11和17
我想知道为什么这段代码编译成功,请
我尝试创建一个实现List和Map接口的类,但是List和Map的remove方法会发生冲突
boolean remove(Object o);
V remove(Object key);
此代码编译成功的原因是类型擦除,这是 Java 泛型的一个功能。类型擦除是在Java代码编译过程中删除与类型参数相关的所有信息的过程。因此,为 getMapInstance() 方法生成的字节码将如下所示:
public static Map getMapInstance() {
Map t = new HashMap();
return (Map) t;
}
由于类型信息在编译过程中被擦除,Java编译器无法检测到返回的对象实际上不是List类型。相反,它是 Map
字节码中的checkcast指令用于确保分配给列表变量的对象确实是List的实例。但是,由于类型信息被删除,因此此检查不会捕获类型不匹配。
当代码尝试将列表变量用作列表时,会在运行时抛出 ClassCastException。此时,Java 虚拟机 (JVM) 检测到类型不匹配并抛出异常。
总而言之,代码之所以能够成功编译,是因为类型擦除,即在编译过程中删除了类型信息。字节码中的校验指令不会捕获类型不匹配,因为类型信息被擦除。当代码尝试将列表变量用作列表时,会在运行时引发 ClassCastException。
需要注意的是,使用原始类型(即不指定类型参数)可能会导致类型不匹配和其他问题。通常建议使用参数化类型并尽可能避免使用原始类型。