Java 使用不同于从泛型集合定义的对象
昨天我在回答问题的时候发现了一些很奇怪的事情 我可以将集合定义为Java 使用不同于从泛型集合定义的对象,java,generics,type-inference,Java,Generics,Type Inference,昨天我在回答问题的时候发现了一些很奇怪的事情 我可以将集合定义为O类型,即使它的实际类型是T。 这可能是因为我使用的是原始类型 然而,最令人惊讶的是,我能够从这个集合中消费,它是一个列表,尽管它显然是一个列表 下面是相关的代码 publicstaticvoidmain(字符串[]args){ String value=“带有numb3r5的字符串”; 函数fn1=列表::of; 函数fn2=x->x.get(0); 函数fn3=x->List.of(x.length()); InputConve
O
类型,即使它的实际类型是T
。
这可能是因为我使用的是原始类型
然而,最令人惊讶的是,我能够从这个集合中消费,它是一个列表
,尽管它显然是一个列表
下面是相关的代码
publicstaticvoidmain(字符串[]args){
String value=“带有numb3r5的字符串”;
函数fn1=列表::of;
函数fn2=x->x.get(0);
函数fn3=x->List.of(x.length());
InputConverter converter=新的InputConverter(值);
List ints=converter.convertBy(fn1、fn2、fn3);
System.out.println(“ints=“+ints”);
System.out.println(“ints.get(0)=”+ints.get(0));
System.out.println(“ints.get(0.getClass()=”+ints.get(0.getClass());
}
公共静态类输入转换器{
私人终审法院;
公共输入转换器(src){
this.src=src;
}
@SuppressWarnings({“unchecked”,“rawtypes”})
公共R转换器(功能…功能){
Function functionsChain=Function.identity();
for(函数:函数){
functionsChain=functionsChain.然后(函数);
}
返回(R)函数调用(src);
}
}
结果如下
ints = [21]
ints.get(0) = 21
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.io.FileInputStream (java.lang.Integer and java.io.FileInputStream are in module java.base of loader 'bootstrap')
at Scratch.main(scratch_8.java:18)
为什么可以从这个集合中消费
我注意到以下内容:在消费时不要抛出异常
System.out.println(“String.valueOf(ints.get(0))=”+String.valueOf(ints.get(0));
System.out.println(“((Object)int.get(0)).toString()=”+((Object)int.get(0)).toString());
然而,以下情况确实如此
System.out.println(“ints.get(0.toString()=”+ints.get(0.toString());
泛型不是java类二进制表示的一部分。泛型是通过casting实现的
“在幕后,编译器替换板条箱中对T的所有引用
换句话说,在代码编译之后,泛型
实际上只是对象类型。”摘自:珍妮·博亚斯基。“OCP
Oracle认证专业Java SE 11开发人员完整研究
指南”。苹果书
这意味着编译器将确保convertBy返回列表,但不能确认所有类型。但编译器将替换:
ints.get(0).toString()
与((FileInputStream)ints.get(0)).toString())
配合使用,以备需要
类似的例子是,具有错误泛型参数的强制转换列表将产生相同的异常
List list = List.of("text");
List<Integer> numbers = (List<Integer>) list;
System.out.println(numbers.get(0).longValue());
List List=List.of(“文本”);
列表编号=(列表)列表;
System.out.println(numbers.get(0.longValue());
结论:
如果我们想使用类型安全代码,我们必须确保不使用原始类型。输入转换器#convertBy使用原始类型函数
声明作为输入,其返回类型使用泛型类型推断
,因此它将解析为指定的类型,在您的情况下,它将是列表
也就是说,您可以将转换后的类型,即,InputConverter\convertBy
的结果分配给您想要的任何类型:List
,List
,List
。。。只要它在运行时与转换类型(最后一个函数结果)的结果兼容,您仍然不会得到任何错误,直到在运行时,JVM将对集合擦除类型执行隐式强制转换(在您的情况下为FileInputStream
),并且由于实际类型为Integer
而失败
下面的语句不会失败,因为您将集合元素向上强制转换为对象
超类型,并从java.lang.Object
类型调用#toString
方法(您在访问任何成员/方法之前强制转换变量):
下面的陈述与前面的陈述几乎相似:
System.out.println("String.valueOf(ints.get(0)) = " + String.valueOf(ints.get(0)));
给定对象#值
方法实现如下:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
另一方面,下面的语句将失败,因为ints.get(0)
将在访问其#toString
成员之前强制转换(checkcast)到已擦除的运行时类型,即FileInputStream
:
System.out.println("ints.get(0).toString() = " + ints.get(0).toString());
你可以这样想:
System.out.println("ints.get(0).toString() = " + ((FileInputStream) ints.get(0)).toString());
你的问题到底是什么?@Turing85我刚刚编辑了它,在最后添加了一个问题。“为什么可以从这个集合中消费*”,因为
+
是为(字符串、对象)
对定义的(即列表元素的确切类型无关紧要)。但是如果我们在假设is是FileInputStream
的情况下调用list元素上的某个方法,那么它的类型必须通过类型转换来确定。好的,但是当我尝试ints.get(0).toString()
时,我得到了一个ClassCastException
,即使它是对象中的一个方法。。。因为您以实际的FileInputStream
访问列表元素(这是列表的泛型类型断言的,请记住:要调用的方法由参数的静态类型决定),并且断言必须由类型转换强制执行。这就是为什么如果我们替换System.out.println(“ints.get(0.getClass()=”+ints.get(0.getClass())),原始程序可以工作的原因代码>带有对象o=int.get(0);System.out.println(“ints.get(0.getClass()=”+o.getClass())代码>()。
System.out.println("ints.get(0).toString() = " + ((FileInputStream) ints.get(0)).toString());