使用JDK 8

时间:2016-11-22 09:21:56

标签: java generics java-8

我有一些类Box的遗留代码,可以将Serializable数据放入MapOracle JRE 1.8 Update 102在使用Oracle JDK 1.7 Update 80编译时在Oracle JDK 1.8 Updater 102上正常运行。但是当我使用get编译它时,它无法正常运行。我在使用通用Box函数时遇到了一些问题。

使用有问题的通用get函数从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"))); } } 实例输出格式化日期的SSCCE:

get

使用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的引用不明确

而其他函数在unchecked or 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 // 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 的警告,我注意到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: 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

编译为1.8:

Class<T> clazz

有人可以解释为什么编译方式不同吗?

PS:我已经通过将get作为<body> <script type="text/javascript" src="file:///Macintosh%20HD/Applications/MAMP/htdocs/thesurebettor/Untitled-2.js"></script> <select id = "typeoption"> <option value="0">Qualifying Bet</option> <option value="1">Free Bet</option> <option value="2">Risk Free Bet</option> </select> <div style='display:none;' id='sreturned'> Stake Returned </div> </body> 函数的附加参数来修复它。

2 个答案:

答案 0 :(得分:8)

你的方法

public <T extends Serializable> T get(String key){

  return (T) this.values.get(key);
}

从根本上被打破,因为它基本上说“无论来电者希望如何,我都会将其归还,只要它可以分配给Serializable”。

有趣的是,我们每隔几周就有类似的破碎方法,the last one just yesterday

关键是,如果你的方法承诺返回调用者的意愿,我可以写:

Date date=box.get("key");

但也

String str=box.get("key");
String[] obj=box.get("key");

对于所有这些类型,DateStringString[]可分配给Serializable。不那么直观,你甚至可以写

Object[] obj=box.get("key");

尽管Object[]不是Serializable,但因为Object[]的子类型可能是Serializable。因此编译器会推断出Object[] & Serializable的{​​{1}}(另请参阅here)。

Java 7和Java 8之间的区别在于,当您将此方法调用作为参数添加到另一个调用(也称为“嵌套方法调用”)时,Java 7编译器不会执行此类型推断。它总是使用类型参数的边界,即T,并发现它必须执行varargs调用。

相比之下,Java 8考虑了所有可能性。它可以推断非数组类型并执行varargs调用,但它也可以推断数组类型并将其直接传递给方法Serializable。规则很简单,非vararg调用总是首选。

修复很简单。不要做出你无法忍受的承诺。

String.format(String,Object[])

让调用者明确地进行类型转换。

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

...

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()。因此,JRE类也从未以这种方式引用Serializable。最值得注意的是,即使ObjectOutputStream.writeObject(…)在其签名中引用Serializable,也只是接受Serializable

答案 1 :(得分:1)

除非你打算让这个代码存储混合对象,否则我建议让这个类通用:

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;

public class Box<T extends Serializable> implements Serializable {

   private HashMap<String, T> values = new HashMap<>();

   public T get(String key){
      return this.values.get(key);
   }

   public void put(String key,
                   T value){

      this.values.put(key,
                      value);
   }

   public static void main(String[] args){

      Box<Date> box = new Box<>();
      box.put("key", new Date());

      System.out.println(String.format("%1$td.%1$tm.%1$tY",
                                       box.get("key")));
   }
}

完成后,我会抛弃Box并使用HashMap<String, ...>