避免方法返回通配符类型

Avoiding methods returning Wildcard types

这是我的应用程序设计,我试图在我的方法中避免 returning 通配符类型,以便我可以调用 returned 对象上的方法。有什么建议么?毕竟,使用静态实用方法来查找和 return 正确的处理程序实现是不是一个糟糕的主意?

问题: 我有一堆请求(以及与之对应的响应)。必须处理这些请求,处理结果(响应)可用于进一步处理。在给定时刻处理哪个请求取决于几个标准(这是 enum)。

我的做法: 我的解决方案是构建多个处理程序,每个处理程序对应一个请求-响应类型组合,并让它们都实现一个带有处理请求方法的通用接口。

事情是这样的:

一个处理程序家族 classes 实现了 Handler 接口:

interface Handler<Q extends Request, R extends Response> {
  R handleRequest(Q);
}

和静态实用方法伴侣 class 以找到符合给定条件的正确处理程序:

class Handlers {
  Handler<? extends Request, ? extends Response> handlerForCriteria(Criteria criteria) {
    switch (criteria):
    ...
    // since there are several handler implementations, I couldn't avoid the wildcard 
    // types for the request and response
  }
}

这里是我需要使用它的地方:

Handler<? extends Request, ? extends Response> matchingHandler = 
    Handlers.handlerForCriteria(criteria);
MyResponse response = matchingHandler.handleRequest(myRequest);

我希望能够像上面那样调用 handleRequest,但显然我不允许这样做,因为 matchingHandler 包含通配符类型(它不能确定是否请求的未知通配符类型与“myRequest”相同。

如有必要,使用带有类型转换的类型化方法:

class Handlers {
    <R extends Request, T extends Response> Handler<R, T> handlerForCriteria(Criteria criteria) {
        Handler<?, ?> handler = someStrategryThatIsUntyped();
        return (Handler<R, T>)handler; // just cast
    }
}

那么,why/when 是演员 "safe" 吗?

有时这种情况既是必要的又是安全的。例如,考虑以下代码:

private Map<Class<?>, Handler<?>> handlers = new HashMap<>();

并且您以保证 class 的类型等于处理程序的类型的方式填充它(这不能在地图的声明中完成):

public <T> void register(Class<T> clazz, Handler<T> handler) {
    handlers.put(clazz, handler);
}

但是根据映射的定义,无法安全地编译查找处理程序的方法:

public <T> Handler<T> findHandler(Class<T> clazz) {
    Handler<?> handler = handlers.get(clazz);
    // Although the compiler can't assert it, the handler is of type T
    // The cast is safe, because we programmatically constrain map's entries
    return (Handler<T>)handler; // safe
}

这就是我要做的:

首先,在大多数情况下,如果不需要仅知道 Response.

的精确类型,则以下解决方案有效
class Handlers {
    static Handler<? super Request,? extends Response> handlerForCriteria(Criteria criteria) {
        return null;
    }
}
class CustomRequest implements Request{}

Handler<? super Request, ? extends Response> handler = Handlers.handlerForCriteria(null);
Response response = handler.handleRequest(new CustomRequest() {});

我发现在这种情况下准确地知道 return 类型在哲学上是错误的。如果您期望 return 类型是 CustomResponse,那么您就依赖于该参数的 handleForCriteria 将始终 return CustomResponse 这一事实。万一以后变了呢。额外的维护工作。

static <E extends Request, T extends Response> Handler<E,T> handlerForCriteria(Criteria criteria) {
        return null;
    }
 Handler<CustomRequest, CustomResponse> handler = Handlers.handlerForCriteria(null);
 CustomResponse response = handler.<CustomResponse>handleRequest(new CustomRequest() {});

如果您非常了解它,那么以上可能是一个解决方案。但我永远不会这样做。我宁愿手动类型转换也不愿在方法级别使用泛型类型参数。