【发布时间】:2017-04-05 21:34:00
【问题描述】:
我有一些带有Box 类的遗留代码,可以将Serializable 数据放入Map,当使用Oracle JDK 1.7 Update 80 编译时,它在Oracle JRE 1.8 Update 102 上运行良好。但是当我用Oracle JDK 1.8 Updater 102 编译它时它不能正常运行。我在使用通用 get 函数时遇到了一些问题。
使用有问题的通用 get 函数从 Box 实例输出格式化日期的 SSCCE:
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")));
}
}
使用 JDK 1.8 编译并使用 JRE 1.8 运行时出现以下异常:
线程 "main" java.lang.ClassCastException 中的异常:java.util.Date 无法转换为 [Ljava.lang.Object; 在 Box.main(Box.java:31)
System.out.println 等一些方法在与get 函数一起使用时会产生编译器错误
错误:对 println 的引用不明确
而其他函数使用get 函数运行良好。
编译器打印出关于unchecked or unsafe operations 的警告,我注意到main 方法被编译为不同的字节码:
用 1.7 编译:
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
用 1.8 编译:
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
有人可以解释为什么它的编译方式不同吗?
PS:我已经通过将Class<T> clazz 作为get 函数的附加参数来修复它。
【问题讨论】:
-
@davida。谢谢。
-
不管有什么错误,你的通用方法都被破坏了。
T正是调用者所期望的;你没有理由相信这个转换会成功(因此,未经检查的警告。)类型 var 只出现在返回类型中的泛型方法几乎总是被破坏,并且只提供类型安全的错觉。最好只返回可序列化(或对象)并让调用者进行转换。 -
@BrianGoetz 你在发帖前读过Holger's answer 吗?如果您有什么要补充的,请随时评论此答案。
-
@fireandfuel 当我发表评论时,没有发布任何答案。