使用Optional.map.orElse返回不同​​的泛型类型

时间:2017-04-03 12:18:08

标签: java spring-mvc generics optional

我使用Spring Boot编写REST服务。 当操作成功并且相应地失败时,我需要返回不同的实体。 Spring中的ResponseEntity按类型T进行参数化。我知道我可以省略类型并仅返回ResponseEntity,但在尝试使用Java 8 Optional的{​​{1}}链创建响应时,这还不够:

orElse

由于Java编译器的类型推断public ResponseEntity getDashboard(String user, UUID uuid) { Optional<Dashboard> dashboard = dashboardService.getDashboard( user, uuid ); // this gives unchecked assignment: 'org.springframework.http.ResponseEntity' // to 'org.springframework.http.ResponseEntity<my.package.SomeClass>' return dashboard .map( ResponseEntity::ok ) .orElse( createNotFoundResponse( uuid, "No such object" ) ); } public static <T> ResponseEntity createNotFoundResp( T entity, String message ) { ResponseMessage<T> responseMessage = new ResponseMessage<>( message, entity ); return ResponseEntity.status( HttpStatus.NOT_FOUND ).body( responseMessage ); } 子句应该返回与可选项不为空时相同的类型,即orElse而不是ResponseEntity<Dashboard>。我试图通过提供不同的返回路径来颠覆这个问题:

ResponseEntity<ResponseMessage>

...但随后Intellij突出显示if ( dashboard.isPresent() ) { return ResponseEntity.ok( dashboard.get() ); } else { return createNotFoundResponse( uuid, "No such object" ); } 部分,并大声说这个块可以简化为上面的那个(导致未经检查的警告)。

有没有办法在没有任何编译器警告和dashboard.isPresent()注释的情况下干净地编写此代码?

1 个答案:

答案 0 :(得分:2)

  

有没有办法在没有任何编译器警告和@SuppressUnchecked注释的情况下干净地编写此代码?

在这种情况下,我不能摆脱编译器警告。一个可能的干净解决方案(至少没有编译器警告)拒绝Optional.map的想法,而赞成简单的if / else?:驱动的策略不可用虽然有流畅的界面。

static <T, U> ResponseEntity<?> okOrNotFound(final Optional<T> optional, final Supplier<? extends U> orElse) {
    return okOrNotFound(optional, "Not found", orElse);
}

static <T, U> ResponseEntity<?> okOrNotFound(final Optional<T> optional, final String message, final Supplier<? extends U> orElse) {
    return optional.isPresent()
            ? status(OK).body(optional.get())
            : status(NOT_FOUND).body(new NotFound<>(orElse.get(), message));
}
@RequestMapping(method = GET, value = "/")
ResponseEntity<?> get(
        @RequestParam("user") final String user,
        @RequestParam("uuid") final UUID uuid
) {
    final Optional<Dashboard> dashboard = dashboardService.getDashboard(user, uuid);
    return okOrNotFound(dashboard, () -> uuid);
}

注意orElse并不是您想要的:orElseGet是懒惰的,只有在给定的可选值不存在时才会调用其供应商。

然而,Spring提供了一种更好的方法来完成你所需要的东西,我相信一种更干净的方式来做这样的事情。请查看为此目的而设计的controller advices

// I would prefer a checked exception having a super class like ContractException
// However you can superclass this one into your custom super exception to serve various purposes and contain exception-related data to be de-structured below
final class NotFoundException
        extends NoSuchElementException {

    private final Object entity;

    private NotFoundException(final Object entity) {
        this.entity = entity;
    }

    static NotFoundException notFoundException(final Object entity) {
        return new NotFoundException(entity);
    }

    Object getEntity() {
        return entity;
    }

}

现在REST控制器方法变为:

@RequestMapping(method = GET, value = "/")
Dashboard get(
        @RequestParam("user") final String user,
        @RequestParam("uuid") final UUID uuid
) {
    return dashboardService.getDashboard(user, uuid)
            .orElseThrow(() -> notFoundException(uuid));
}

Spring非常聪明,可以将对象转换为status(OK).body(T)本身,因此我们只是抛出一个包含我们感兴趣的单个对象的异常。接下来,示例控制器异常建议可能如下所示:

@ControllerAdvice
final class ExceptionControllerAdvice {

    @ExceptionHandler(NotFoundException.class)
    ResponseEntity<NotFound<?>> acceptNotFoundException(final NotFoundException ex) {
        return status(NOT_FOUND).body(notFound(ex));
    }

}

其中notFound()方法的实现如下:

static NotFound<?> notFound(final NotFoundException ex) {
    return notFound(ex, "Not found");
}

static NotFound<?> notFound(final NotFoundException ex, final String message) {
    return new NotFound<>(ex.getEntity(), message);
}

对于我的尖峰项目,提供以下结果:

  • _http:// localhost:8080 /?user = owner&amp; uuid = 00000000-0000-0000-0000-000000000000 - {"description":"dashboard owned by owner"}
  • _http:// localhost:8080 /?user = user&amp; uuid = 00000000-0000-0000-0000-000000000000 - {"entity":"00000000-0000-0000-0000-000000000000","message":"Not found"}