使用JDK 8编译通用方法时发生故障 [英] Broken cast on compiling generic method with JDK 8
问题描述
我有类 Box
的遗留代码,用于将 Serializable
数据放入 Map
,它在使用 Oracle JDK 1.7 Update 80 $ c编译时在
Oracle JRE 1.8 Update 102
上运行正常$ C>。但是,当我使用 Oracle JDK 1.8更新程序102
进行编译时,它无法正常运行。我有一个通用的 get
函数的问题。
一个SSCCE,它从 使用JDK编译时,出现以下异常1.8和我使用JRE 1.8运行它: 线程main中的异常java.lang.ClassCastException:java.util.Date不能是投射到[Ljava.lang.Object; 一些方法如System.out.println会产生编译器错误当与 错误:对println的引用不明确 p> 其他函数在 编译器输出一个关于 编译为1.7: 编译为1.8: 有人可以解释为什么它的编译方式不同吗? b 通过给予 您的方法 基本上被打破了,因为它基本上说无论呼叫者希望如何,它只要它可以赋值给 有趣的是,我们每隔几周就会有类似的破坏方法在这里,昨天的最后一个。 关键点是,如果你的方法承诺返回任何调用者的愿望,我可以写: 还有 所有这些类型 尽管 Java 7与Java 8的区别在于,Java 7编译器在执行此类型推断时您将此方法调用作为参数传递给另一个调用(又名嵌套方法调用)。它始终使用类型参数的边界,即 相比之下,Java 8考虑了所有可能性。它可以推断非数组类型并执行可变参数调用,但它也可以推断数组类型并将其直接传递给方法 修复很简单。 并让调用者明确地进行类型转换。 或者在需要任意对象时不投射: 这就是一个复杂的变体 或者,您可以使用 ... 顺便说一下,明确指出 I have some legacy code with class A SSCCE which outputs a formatted date from a I get the following exception when it is compiled with JDK 1.8 and I run it with JRE 1.8: Exception in thread "main" java.lang.ClassCastException: java.util.Date cannot be cast to [Ljava.lang.Object;
at Box.main(Box.java:31) Some Methods like System.out.println produces a compiler error when used with the error: reference to println is ambiguous while other function runs fine with the The compiler prints out a warning about Compiled with 1.7: Compiled with 1.8: Can somebody explain why it is compiled differently? PS: I already fixed it by giving Your method is fundamentally broken as it basically says "whatever the caller wishes, I will return it, as long as it is assignable to Interestingly, we have similar broken methods every few weeks here, the last one just yesterday. The key point is, if your method promises to return whatever the caller wishes, I could write: but also As all these types, despite The difference between Java 7 and Java 8 is that the Java 7 compiler did not perform this type inference when you put this method invocation as an argument to another invocation (aka "nested method call"). It always used the bounds of the type parameter, i.e. In contrast, Java 8 considers all possibilities. It can infer a non-array type and perform a varargs invocation, but it can also infer an array type and pass it directly to the method The fix is simple. Don’t make promises you can’t hold. and let the caller do the type cast explicitly. or no cast when an arbitrary object is needed: which is by the way a convoluted variant of Alternatively, you can use a … By the way, referring to 这篇关于使用JDK 8编译通用方法时发生故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! b
$ import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
public class Box实现Serializable {
private HashMap< String,Serializable> values = new HashMap< String,Serializable>();
public< T extends Serializable> T get(String key){
return(T)this.values.get(key);
}
public void put(String key,
Serializable value){
this.values.put(key,
value) ;
public static void main(String [] args){
Box box = new Box();
box.put(key,
new Date());
System.out.println(String.format(%1 $ td。%1 $ tm。%1 $ tY,
box.get(key)));
$ / code $ / pre>
在Box.main(Box.java:31)
获取
函数一起使用时
$ b
get
函数中正常运行。
unchecked或unsafe operations
的警告,我注意到主要方法被编译为不同的字节码:
public static void main(java.lang.String [ ]);
Code:
0:new#8 // class Box
3:dup
4:invokespecial#9 // Method< init>:()V
7:astore_1
8:aload_1
9:ldc#10 // String key
11:new#11 // class java / util / Date
14:dup
15:invokespecial#12 //方法java / util / Date。< init>:()V
18:invokevirtual#13 //方法put :( Ljava / lang / String; Ljava / io / Serializable;)V
21:getstatic#14 //字段java / lang / System.out:Ljava / io / PrintStream;
24:ldc#15 // String%1 $ td。%1 $ tm。%1 $ tY
26:iconst_1
27:anewarray#16 // class java / lang / Object
30:dup
31:iconst_0
32:aload_1
33:ldc#10 //字符串键$ b $ 35:invokevirtual#17 //方法get :( Ljava /郎/字符串;)Ljava / IO /串行化;
38:aastore
39:invokestatic#18 //方法java / lang / String.format:(Ljava / lang / String; [Ljava / lang / Object;)Ljava / lang / String;
42:invokevirtual#19 //方法java / io / PrintStream.println:(Ljava / lang / String;)V
45:return
public static void main(java。 lang.String []);
Code:
0:new#8 // class Box
3:dup
4:invokespecial#9 // Method< init>:()V
7:astore_1
8:aload_1
9:ldc#10 // String key
11:new#11 // class java / util / Date
14:dup
15:invokespecial#12 //方法java / util / Date。< init>:()V
18:invokevirtual#13 //方法put :( Ljava / lang / String; Ljava / io / Serializable;)V
21:getstatic#14 //字段java / lang / System.out:Ljava / io / PrintStream;
24:ldc#15 //字符串%1 $ td。%1 $ tm。%1 $ tY
26:aload_1
27:ldc#10 //字符串键
29:invokevirtual#16 //方法get:(Ljava / lang / String;)Ljava / io / Serializable;
32:checkcast#17 // class[Ljava / lang / Object;
35:invokestatic#18 //方法java / lang / String.format:(Ljava / lang / String; [Ljava / lang / Object;)Ljava / lang / String;
38:invokevirtual#19 //方法java / io / PrintStream.println:(Ljava / lang / String;)V
41:return
$ b Class< T>来修复它。 clazz
作为获取
函数的附加参数。
public< T extends Serializable> T get(String key){
return(T)this.values.get(key);
}
Serializable
。
Date date = box.get(key );
String str = box.get(key);
String [] obj = box.get(key);
Date
, String
或 String []
可赋值给 Serializable
。不太直观的是,你甚至可以写出
Object [] obj = box.get(key);
Object []
不是 Serializable
,因为可能存在 Object []
的子类型,它是 Serializable
。所以编译器会推断出 Object []&可串行化用于
T
(另请参阅此处)。
Serializable
,并发现它必须执行可变参数调用。
String.format(String,Object [])
。规则很简单,总是首选非可变参数调用。
public Serializable get(String key){
return this。 values.get(键);
}
日期日期=(日期)box.get(key);
System.out.println(String.format(%1 $ td。%1 $ tm。%1 $ tY,box.get(key))) ;
System.out.printf(%1 $ td。%1 $ tm。%1 $ tY%n,box.get(key));
Class
对象以指定预期的类型:
public< T extends Serializable> T get(String key,Class< T> type){
return type.cast(this.values.get(key));
}
Date date = box.get(key,Date.class);
Serializable
没有真正的好处。有很多地方可以返回可序列化的对象,例如,参见 Collections.emptyList()
,而不需要声明 Serializable
。因此,JRE类永远不会以这种方式引用 Serializable
。最值得注意的是,甚至没有 ObjectOutputStream.writeObject(...)
在其签名中引用 Serializable
,但只接受 Object
。Box
to put and get Serializable
data into a Map
, which runs fine on Oracle JRE 1.8 Update 102
when compiled with Oracle JDK 1.7 Update 80
. But it don't run properly when I compile it with Oracle JDK 1.8 Updater 102
. I had some problems with a generic get
function.Box
instance using a problematic generic get
function:import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
public class Box implements Serializable{
private HashMap<String, Serializable> values = new HashMap<String, Serializable>();
public <T extends Serializable> T get(String key){
return (T) this.values.get(key);
}
public void put(String key,
Serializable value){
this.values.put(key,
value);
}
public static void main(String[] args){
Box box = new Box();
box.put("key",
new Date());
System.out.println(String.format("%1$td.%1$tm.%1$tY",
box.get("key")));
}
}
get
function
get
function.unchecked or unsafe operations
and I noticed the main method is compiled to different byte code: public static void main(java.lang.String[]);
Code:
0: new #8 // class Box
3: dup
4: invokespecial #9 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #10 // String key
11: new #11 // class java/util/Date
14: dup
15: invokespecial #12 // Method java/util/Date."<init>":()V
18: invokevirtual #13 // Method put:(Ljava/lang/String;Ljava/io/Serializable;)V
21: getstatic #14 // Field java/lang/System.out:Ljava/io/PrintStream;
24: ldc #15 // String %1$td.%1$tm.%1$tY
26: iconst_1
27: anewarray #16 // class java/lang/Object
30: dup
31: iconst_0
32: aload_1
33: ldc #10 // String key
35: invokevirtual #17 // Method get:(Ljava/lang/String;)Ljava/io/Serializable;
38: aastore
39: invokestatic #18 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
42: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
public static void main(java.lang.String[]);
Code:
0: new #8 // class Box
3: dup
4: invokespecial #9 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #10 // String key
11: new #11 // class java/util/Date
14: dup
15: invokespecial #12 // Method java/util/Date."<init>":()V
18: invokevirtual #13 // Method put:(Ljava/lang/String;Ljava/io/Serializable;)V
21: getstatic #14 // Field java/lang/System.out:Ljava/io/PrintStream;
24: ldc #15 // String %1$td.%1$tm.%1$tY
26: aload_1
27: ldc #10 // String key
29: invokevirtual #16 // Method get:(Ljava/lang/String;)Ljava/io/Serializable;
32: checkcast #17 // class "[Ljava/lang/Object;"
35: invokestatic #18 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
38: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
41: return
Class<T> clazz
as additional parameter to the get
function.public <T extends Serializable> T get(String key){
return (T) this.values.get(key);
}
Serializable
".Date date=box.get("key");
String str=box.get("key");
String[] obj=box.get("key");
Date
, String
, or String[]
are assignable to Serializable
. Less intuitively, you can even writeObject[] obj=box.get("key");
Object[]
is not Serializable
, because there could be a subtype of Object[]
that is Serializable
. So the compiler will infer Object[] & Serializable
for T
(see also here).
Serializable
and found that it has to perform a varargs invocation.String.format(String,Object[])
. The rules are simple, a non-vararg invocation is always preferred.public Serializable get(String key) {
return this.values.get(key);
}
Date date=(Date)box.get("key");
System.out.println(String.format("%1$td.%1$tm.%1$tY", box.get("key")));
System.out.printf("%1$td.%1$tm.%1$tY%n", box.get("key"));
Class
object to specify the expected type:public <T extends Serializable> T get(String key, Class<T> type) {
return type.cast(this.values.get(key));
}
Date date=box.get("key", Date.class);
Serializable
explicitly has no real benefit. There are plenty of place, where serializable objects are returned, see Collections.emptyList()
, for example, without declaring Serializable
. Consequently, the JRE classes never refer to Serializable
this way either. Most notably, not even ObjectOutputStream.writeObject(…)
refers to Serializable
in its signature, but just accepts Object
.