简体   繁体   English

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

[英]Returning different generic type with Optional.map.orElse

I use Spring Boot to write a REST service. 我使用Spring Boot编写REST服务。 I need to return a different entity when an operation succeeds and fails accordingly. 当操作成功并因此失败时,我需要返回一个不同的实体。 ResponseEntity in Spring is parametrized by type T . Spring中的ResponseEntityT类型设置。 I know I can omit the type and return just ResponseEntity , but that is not enough when trying to create the response with Java 8 Optional 's orElse chain: 我知道我可以省略类型并仅返回ResponseEntity ,但是在尝试使用Java 8 OptionalorElse链创建响应时,这还不够:

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 );
}

Due to Java compiler's type inference orElse clause should return the same type as when the optional is not empty, ie ResponseEntity<Dashboard> and not ResponseEntity<ResponseMessage> . 由于Java编译器的类型推断, orElse子句应返回与可选字段不为空时相同的类型,即ResponseEntity<Dashboard>而不是ResponseEntity<ResponseMessage> I tried to subvert this problem, by providing different return paths like this: 我试图通过提供不同的返回路径来颠覆这个问题:

if ( dashboard.isPresent() ) {
    return ResponseEntity.ok( dashboard.get() );
} else {
    return createNotFoundResponse( uuid, "No such object" );
}

...but then Intellij highlights the dashboard.isPresent() part and shouts that this block can be simplified to the one above (which results in unchecked warning). ...但是随后Intellij突出显示了dashboard.isPresent()部分,并大喊该块可以简化为上面的块(这将导致未经检查的警告)。

Is there a way to write this code cleanly without any compiler warnings and @SuppressUnchecked annotations? 有没有办法在没有任何编译器警告和@SuppressUnchecked注释的情况下干净地编写此代码?

Is there a way to write this code cleanly without any compiler warnings and @SuppressUnchecked annotations? 有没有办法在没有任何编译器警告和@SuppressUnchecked注释的情况下干净地编写此代码?

I don't think you can get rid of compiler warnings in this case. 在这种情况下,我认为您无法摆脱编译器警告。 One of possible clean solutions (at least, no compiler warnings) is rejecting the idea of Optional.map in favor of a simple if / else or ?: -driven strategy not available with fluent interfaces though. 一种可能的简洁解决方案(至少没有编译器警告)是拒绝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);
}

Note orElse is not really what you wanted: orElseGet is lazy and only invokes its supplier if the given optional value is not present. 请注意, orElse并不是您真正想要的: orElseGet是惰性的,并且仅在不存在给定的可选值时才调用其供应商。

However, Spring features a better way to accomplish what you need and I believe a cleaner way of doing the things like that. 但是,Spring提供了一种更好的方式来满足您的需求,并且我相信这样做的方式会更清洁。 Take a look at controller advices that are designed for such purposes. 查看针对此类目的而设计的控制器建议

// 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;
    }

}

Now the REST controller method becomes: 现在,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 is smart enough to convert objects to status(OK).body(T) itself, so we're just throwing an exception containing a single object we are interested in. Next, a sample controller exception advice might look as follows: 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));
    }

}

where notFound() method is implemented like this: 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);
}

For my spike project provides the following results: 为我的秒杀项目提供以下结果:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM