你的方法
public <T extends Serializable> T get(String key){
return (T) this.values.get(key);
}
从根本上来说,它基本上是说“无论调用者希望什么,我都会返回它,只要它可以分配给Serializable
”.
有趣的是,我们每隔几周就会有类似的损坏方法,最后一张就在昨天 https://stackoverflow.com/q/40703415/2711488.
关键是,如果你的方法承诺返回调用者希望的任何内容,我可以写:
Date date=box.get("key");
but also
String str=box.get("key");
String[] obj=box.get("key");
与所有这些类型一样,Date
, String
, or String[]
可分配给Serializable
。不太直观,你甚至可以写
Object[] obj=box.get("key");
despite Object[]
is not Serializable
,因为可能有一个子类型Object[]
那是Serializable
。所以编译器会推断Object[] & Serializable
for T
(也可以看看here https://stackoverflow.com/a/36403072/2711488).
Java 7 和 Java 8 之间的区别在于,当您将此方法调用作为另一个调用(也称为“嵌套方法调用”)的参数时,Java 7 编译器不会执行此类型推断。它始终使用类型参数的边界,即Serializable
并发现它必须执行可变参数调用。
相比之下,Java 8 考虑了所有可能性。它可以推断非数组类型并执行可变参数调用,但它也可以推断数组类型并将其直接传递给方法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()
,例如,没有声明Serializable
。因此,JRE 类从不引用Serializable
也可以这样。最值得注意的是,甚至没有ObjectOutputStream.writeObject(…) https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html#writeObject-java.lang.Object-指的是Serializable
在其签名中,但只是接受Object
.