繁体   English   中英

Java泛型,类型擦除,通配符和函数 <?…> 产生不兼容的类型

[英]Java Generics, Type Erasure, wildcard and Function<?…> produce incompatible types

还有一个菜鸟问题,抱歉。

我们考虑以下代码:

public class ExceptionHandler {

   // simple internal manager
   @FunctionalInterface
   private interface ExceptionManager<D extends Exception> {
     int getErrorCode(D e, WebRequest request, 
                      HttpServletRequest servletRequest);
   }

   // One field, just for the illustration 
   // (TypeMismatchException came from spring framework)
   private ExceptionManager<TypeMismatchException> tmeManager = 
      (ex, req, servletRequest) -> {
         int errorCode = 0;
         // ...
         return errorCode;
      };

   // A simple "factory" for an ExceptionManager
   private Function<? extends Exception, 
          Optional<ExceptionManager<? extends Exception>>> factory = (ex) -> {
      if(ex instanceof TypeMismatchException) {
         return Optional.of(tmeManager);
      }
      /* ... */
      return Optional.empty();
   };

   // global  exception manager
   private ExceptionManager<? extends Exception> defaultExceptionManager =
      (exception, request, servletRequest) -> {

         final Optional<ExceptionManager<? extends Exception>> manager = 
                         factory.apply(exception);

         if(manager.isPresent()) {
            return manager.get()
                    .getErrorCode(exception, request, servletRequest);
         }
         return 1;
      };
}

以下代码无法编译。 实际上是关于类型不兼容问题的抱怨。

Error:(...) java: incompatible types: java.lang.Exception
      cannot be converted to capture#1 of ? extends java.lang.Exception
Error:(...) java: incompatible types: java.lang.Exception
      cannot be converted to capture#2 of ? extends java.lang.Exception

在思考和阅读有关问题之后,似乎java执行类型擦除(用于jvm向后兼容性),因此代码:

private ExceptionManager<? extends Exception> defaultExceptionManager = 
                   (exception, request, servletRequest) -> { /* ... */ }

成为

private ExceptionManager<Exception> defaultExceptionManager = 
                   (exception, request, servletRequest) -> { /* ... */ }

这实际上是将getErrorCode的第一个参数(即exception )修复为Exception

据我所知(实际上并不确定真正理解),对于泛型类型,该过程应该是相同的。 从而

private interface ExceptionManager<D extends Exception> { /* ... */ }

应该成为

private interface ExceptionManager<Exception> { /* ... */ }

并因此也将getErrorCode方法中的参数e修复为Exception 在(如果我是对的)之后,类型不兼容性问题变得更加清晰。 但是,我仍然怀疑capture#xx of ? extends Exception capture#xx of ? extends Exception因为这意味着(仍然根据我的理解)类型擦除对整个代码部分无效。

有人能指出我的代码中的错误(可能是一个文档,我可以找到一些关于编译器的内部行为的泛型,通配符和类型擦除的解释)?

注意 :代码也抱怨不兼容的类型。

protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex,
   final HttpHeaders headers, final HttpStatus status,
   final WebRequest request) {
   /* ... */
   int errorCode = defaultExceptionManager.getErrorCode(ex, request, servletRequest);
}

这个电话的结果是

Error:(154, 63) java: incompatible types:
      org.springframework.beans.TypeMismatchException
      cannot be converted to capture#3 of ? extends java.lang.Exception

对不起这个问题的长度,感谢阅读和回答! 问候

当你声明一个函数Function<? extends Exception, …> Function<? extends Exception, …> ,你说参数的类型是未知的,因此你不能apply这个函数,因为你不知道实际参数是否与未知参数类型兼容。 这同样适用于ExceptionManager<? extends Exception> ExceptionManager<? extends Exception> ,它接收一个未知的异常类型作为参数。

这与不知道返回类型不同,就像函数返回时一样? extends R ? extends R ,你仍然知道结果可以赋予R或超类型的R

传入参数和结果类型之间存在关系,如果它是通用的,则会使该代码可用,但是,您不能使变量(保持对Function的引用)通用。 您可以使用普通方法解决此问题,这些方法可以声明类型参数。 这几乎是直截了当的,因为你在这里过度使用函数:

public class ExceptionHandler {
    // simple internal manager
    @FunctionalInterface
    private interface ExceptionManager<D extends Exception> {
        int getErrorCode(D e, WebRequest request, HttpServletRequest servletRequest);
    }
    // One field, just for the illustration 
    private static ExceptionManager<TypeMismatchException> tmeManager = 
       (ex, req, servletRequest) -> {
          int errorCode = 0;
          // ...
          return errorCode;
       };

    // A simple "factory" for an ExceptionManager
    private static <E extends Exception> Optional<ExceptionManager<E>> factory(E ex) {
        if(ex instanceof TypeMismatchException) {
            // unavoidable unchecked operation
            @SuppressWarnings("unchecked") ExceptionManager<E> em
                                         = (ExceptionManager<E>)tmeManager;
            return Optional.of(em);
        }
        /* ... */
        return Optional.empty();
    }
    // global  exception manager
    private ExceptionManager<Exception> defaultExceptionManager
                                      = ExceptionHandler::handleDefault;

    static <E extends Exception> int handleDefault(E exception, WebRequest request, 
                                                   HttpServletRequest servletRequest) {
        final Optional<ExceptionManager<E>> manager = factory(exception);
        return manager.map(em -> em.getErrorCode(exception, request, servletRequest))
                      .orElse(1);
    }
}

当返回通过检查instanceof发现适合的特定处理程序时,有一个地方未经检查的操作是不可避免的。 这里必须小心,因为异常可能是子类型TypeMismatchException 实例也可能是运行时的TypeMismatchException ,但调用者已将其超类型替换为E 后者是更危险的情况,因为通用签名将承诺能够处理比实际更广泛的类型。 只要该方法是private ,您就可以轻松地概述调用者仅传递与用于检查的相同实例,因此它将起作用。

暂无
暂无

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

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