Java泛型;“向上投射”;非参数化类型
为什么只有在取消对Main.Main()中的第三条语句的注释时才得到ClassCastException?没有例外,但执行良好的第一和第二条语句Java泛型;“向上投射”;非参数化类型,java,generics,compilation,jvm,rtti,Java,Generics,Compilation,Jvm,Rtti,为什么只有在取消对Main.Main()中的第三条语句的注释时才得到ClassCastException?没有例外,但执行良好的第一和第二条语句 public class Tuple<K, V> { public final K first; public final V second; public Tuple(K first, V second) { this.first = first; this.second = second; } @Ov
public class Tuple<K, V> {
public final K first;
public final V second;
public Tuple(K first, V second) {
this.first = first;
this.second = second;
}
@Override public String toString() {
return "Tuple{" + "first = " + first + ", second = " + second + '}';
}
}
class Test { static Tuple f(){return new Tuple("test", 8);} }
class Bar {}
class Main{
public static void main(String[] args) {
Tuple<String, Bar> t = Test.f();
System.out.println(t);
//System.out.println(t.second.getClass().getSimpleName());
}
}
公共类元组{
公开决赛K第一;
公开决赛V秒;
公共元组(K第一,V第二){
this.first=first;
这个秒=秒;
}
@重写公共字符串toString(){
返回“Tuple{”+“first=“+first+”,second=“+second+'}”;
}
}
类测试{静态元组f(){返回新元组(“Test”,8);}
类条{}
班长{
公共静态void main(字符串[]args){
元组t=Test.f();
系统输出打印ln(t);
//System.out.println(t.second.getClass().getSimpleName());
}
}
提前感谢。根据,对于Object
类中的getClass()
方法
根据,对于对象中的getClass()
方法,实际结果类型为类
编写方法调用链时,实际结果类型为类:
System.out.println(t.second.getClass().getSimpleName());
编译器有效地将其扩展为:
TypeOfTSecond tmpTSecond = t.second;
Class<?> clazzTmp = tmp.getClass();
String nameTmp = clazzTmp.getSimpleName();
System.out.println(nameTmp);
因此,即使您从未访问任何特定于条的功能,您也会得到ClassCastException
为了演示这一点,下面是字节码:
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method Test.f:()LTuple;
3: astore_1
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: getfield #4 // Field Tuple.second:Ljava/lang/Object;
11: checkcast #5 // class Bar
14: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class;
17: invokevirtual #7 // Method java/lang/Class.getSimpleName:()Ljava/lang/String;
20: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: return
第8行是将t.second
推到堆栈上的位置;第11行是转换到条的位置
这只是因为在声明test.f()
时使用了原始类型:
如果这被正确地声明为
static Tuple<String, Integer> f(){return new Tuple<>("test", 8);}
当我重新编译该标志时:
Main.java:19: warning: [unchecked] unchecked call to Tuple(K,V) as a member of the raw type Tuple
return new Tuple("test", 8);
^
where K,V are type-variables:
K extends Object declared in class Tuple
V extends Object declared in class Tuple
Main.java:26: warning: [unchecked] unchecked conversion
Tuple<String, Bar> t = Test.f();
^
required: Tuple<String,Bar>
found: Tuple
2 warnings
Main.java:19:警告:[未选中]未选中对作为原始类型元组成员的元组(K,V)的调用
返回新元组(“test”,8);
^
其中K,V是类型变量:
扩展类元组中声明的对象
V扩展类元组中声明的对象
Main.java:26:警告:[未选中]未选中的转换
元组t=Test.f();
^
必需:元组
发现:元组
2警告
编写方法调用链时:
System.out.println(t.second.getClass().getSimpleName());
编译器有效地将其扩展为:
TypeOfTSecond tmpTSecond = t.second;
Class<?> clazzTmp = tmp.getClass();
String nameTmp = clazzTmp.getSimpleName();
System.out.println(nameTmp);
因此,即使您从未访问任何特定于条的功能,您也会得到ClassCastException
为了演示这一点,下面是字节码:
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method Test.f:()LTuple;
3: astore_1
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: getfield #4 // Field Tuple.second:Ljava/lang/Object;
11: checkcast #5 // class Bar
14: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class;
17: invokevirtual #7 // Method java/lang/Class.getSimpleName:()Ljava/lang/String;
20: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: return
第8行是将t.second
推到堆栈上的位置;第11行是转换到条的位置
这只是因为在声明test.f()
时使用了原始类型:
如果这被正确地声明为
static Tuple<String, Integer> f(){return new Tuple<>("test", 8);}
当我重新编译该标志时:
Main.java:19: warning: [unchecked] unchecked call to Tuple(K,V) as a member of the raw type Tuple
return new Tuple("test", 8);
^
where K,V are type-variables:
K extends Object declared in class Tuple
V extends Object declared in class Tuple
Main.java:26: warning: [unchecked] unchecked conversion
Tuple<String, Bar> t = Test.f();
^
required: Tuple<String,Bar>
found: Tuple
2 warnings
Main.java:19:警告:[未选中]未选中对作为原始类型元组成员的元组(K,V)的调用
返回新元组(“test”,8);
^
其中K,V是类型变量:
扩展类元组中声明的对象
V扩展类元组中声明的对象
Main.java:26:警告:[未选中]未选中的转换
元组t=Test.f();
^
必需:元组
发现:元组
2警告
我的理解是,这在Java中不起作用。你的泛型没有被赋予类型,它们是泛型。如果您有类似于public final K first的东西,那么这将起作用它将返回字符串。Test.f()
返回原始类型。如果避免使用原始类型,则更容易捕获此错误。Test.f()
的返回类型应定义为Tuple
,以匹配所返回的值。这将导致Tuple t=Test.f()上出现编译错误。
。类型安全是一个强大的功能。您应该发布完整的错误:线程“main”java.lang.ClassCastException中的异常:java.lang.Integer无法转换为Bar
-现在它变得更清晰了…请阅读。我的理解是,这在java中不起作用。你的泛型没有被赋予类型,它们是泛型。如果您有类似于public final K first的东西,那么这将起作用它将返回字符串。Test.f()
返回原始类型。如果避免使用原始类型,则更容易捕获此错误。Test.f()
的返回类型应定义为Tuple
,以匹配所返回的值。这将导致Tuple t=Test.f()上出现编译错误。
。类型安全是一个强大的功能。您应该发布完整的错误:线程“main”java.lang.ClassCastException中的异常:java.lang.Integer无法转换为Bar
-现在它变得更清晰了…请阅读。注意,在这种情况下,编译器不需要将t.second
转换为Bar
。这取决于实现。请注意,在这种情况下,编译器不需要插入t.second
到Bar
的强制转换。也就是说,这样做依赖于实现。向下投票是没有用的,除非有一个向下投票的人澄清isI没有向下投票的问题,但我猜这是因为您的修复基于原始类型的使用。如果Test.f()
声明的类型参数与返回的值相匹配(
),则使用
时会出现编译错误。虽然它可以工作,但它会鼓励糟糕的设计,因为它避免了实际问题(缺乏类型安全性),这通过您执行此操作时收到的警告消息显示出来。我个人不会因为这个原因投反对票(它仍然有效),但有些人比其他人更严格。我明白了!因此,在这里,我们不仅要提供修复并解释修复背后的原因,还必须提出最佳实践建议。正如你所说,我没有说不鼓励使用原始类型。谢谢确切地我个人觉得它不值得投反对票,因为它仍然有效,但如果答案鼓励更好的实践,我可能会投反对票。@VinceEmigh,我用附加注释更新了我的答案。如果不
Main.java:19: warning: [unchecked] unchecked call to Tuple(K,V) as a member of the raw type Tuple
return new Tuple("test", 8);
^
where K,V are type-variables:
K extends Object declared in class Tuple
V extends Object declared in class Tuple
Main.java:26: warning: [unchecked] unchecked conversion
Tuple<String, Bar> t = Test.f();
^
required: Tuple<String,Bar>
found: Tuple
2 warnings