我承认,我们也被吸引使用这种技术。 它非常方便,因为它可以避免看似不必要的转换。 这里是以下技术:
interface SomeWrapper {
<T> T get();
}
现在,您可以安全地将包装器中的任何内容分配给任何类型:
SomeWrapper wrapper = ...
// Obviously
Object a = wrapper.get();
// Well...
Number b = wrapper.get();
// Risky
String[][] c = wrapper.get();
// Unprobable
javax.persistence.SqlResultSetMapping d =
wrapper.get();
实际上,这是您在使用jOOR时可以使用的API, jOOR是我们编写的开源反射库 ,旨在改善集成测试。 使用jOOR,您可以编写如下内容:
Employee[] employees = on(department)
.call("getEmployees").get();
for (Employee employee : employees) {
Street street = on(employee)
.call("getAddress")
.call("getStreet")
.get();
System.out.println(street);
}
该API非常简单。 on()
方法包装Object
或Class
。 然后, call()
方法使用反射在该对象上调用方法(但不需要确切的签名,不需要方法是公共的,并且不引发任何检查的异常)。 并且无需强制转换,然后可以调用get()
将结果分配给任何任意引用类型。
对于像jOOR这样的反射库,这可能是可以的,因为整个库并不是真正的类型安全。 不可能,因为是反射。
但是这种“肮脏”的感觉依然存在。 给调用站点一个关于结果类型的承诺的感觉,这种承诺无法兑现,这将导致ClassCastException
–这是过去在Java 5和泛型之后开始的初级开发人员几乎不知道的事情。
但是JDK库也可以做到这一点……
是的,他们有。 但是很少,并且仅当泛型类型参数确实无关紧要时。 例如,当获取Collection.emptyList()
,其实现如下所示:
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
确实, EMPTY_LIST
是从List
到List<T>
不安全地转换的,但是从语义的角度来看,这是一个安全的转换。 您不能修改此List
引用,并且因为它为空,所以List<T>
中没有任何方法可以给您T
或T[]
的实例,这些实例与您的目标类型不对应。 因此,所有这些都是有效的:
// perfectly fine
List<?> a = emptyList();
// yep
List<Object> b = emptyList();
// alright
List<Number> c = emptyList();
// no problem
List<String[][]> d = emptyList();
// if you must
List<javax.persistence.SqlResultSetMapping> e
= emptyList();
因此,与往常一样( 或大多数情况下 ),JDK库设计人员非常注意不要对您可能会得到的泛型类型做出任何虚假承诺。 这意味着您经常会得到一个Object类型,而您知道另一个类型会更适合。
但是即使您知道这一点,编译器也不会。 删除有一定的价格,而当包装纸或收藏品为空时,必须支付费用。 无法知道此类表达式的内含类型,因此请不要假装自己知道。 换一种说法:
不要使用只是避免广播的通用方法反模式
翻译自: https://www.javacodegeeks.com/2015/05/this-common-api-technique-is-actually-an-anti-pattern.html