在这种情况下,超级和扩展的更惯用的用途是什么?



我正在贡献的一个库维护一个可以接受这些类实例的Classes和处理程序的注册表,想想Consumer<T>。有一个工具用于注册类型*及其所有子类型的处理程序',例如

<T> void registerHandler(Class<T> type, Consumer<? super T>) { ... }

此外,还有一种方法可以接收给定类型的相应处理程序:

<T> Consumer<T> getHandler(Class<? extends T> type)

现在我可以像这样注册一个处理程序:

Consumer<Object> printer = System.out::println;
registerHandler(Object.class, printer);
Consumer<String> screamer = s -> System.out.println(s.toUpperCase());
registerHandler(String.class, screamer);

检索处理程序也适用于子类型:

getHandler(Object.class) // => printer
getHandler(String.class) // => screamer
getHandler(Integer.class) // => printer, because Integer is a subtype of Object

我的问题是关于getHandler方法,特别是它对通配符类型的使用。我看到了另外两种使用它们的有效方法:

<T> Consumer<T> getHandler(Class<? extends T> type) // option 1, from above
<T> Consumer<? super T> getHandler(Class<T> type) // option 2
<T> Consumer<? super T> getHandler(Class<? extends T> type) // option 3
// <T> Consumer<T> getHandler(Class<T> type) // non-option, does not work for subtypes.

表示类型之间这种关系的最佳方式是什么?

我发现使用Class<? extends T>的一个优点是以下工作:

void handle(T obj, Class<? extends T> type) {
Consumer<? super Object> handler = getHandler(type);
// or Consumer<Object>, depending on whether ? super is in the return type or not
handler.accept(obj);
}
Object o = ...;
handle(o, o.getClass());

如果没有? extends T,对handlegetHandler内的调用将是无效的,除非它也使用Class<T>,在这种情况下,带有getClass的调用将是无效的。

<T> Consumer<T> getHandler(Class<? extends T> type)

这是最佳选择,因为返回类型中没有通配符。

返回类型中的通配符很烦人,因为您无法"摆脱"它们。如果将返回值分配给变量,则该变量的声明中也必须包含通配符。

在Effective Java中有一些关于这一点的东西(不要用手指向特定的项目(,它说了一些类似"如果你在返回类型中有通配符,那么你的设计可能有问题"的内容。

(当然,您仍然可以将返回值分配给具有通配符类型的变量;但您不必这样做(。

相关内容

最新更新