Los genéricos de Java, Tipo Erasure, comodín y función <? ...> producen tipos incompatibles

ollie314:

Una pregunta más novato, lo siento por eso.

Consideremos el siguiente código:

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

El siguiente código no puede compilar. En realidad es un problema quejas sobre el tipo de incompatibilidad.

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

Después de pensar y leer sobre el problema, parece que java realizar un borrado tipo (por compatibilidad con versiones anteriores JVM) y por lo tanto el código:

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

convirtió

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

Que en realidad es fijar el primer parámetro de la getErrorCode(a saber exception) a Exception.

Según tengo entendido (no estoy seguro de entender realmente en realidad), el proceso debe ser el mismo para el tipo genérico. Así

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

En caso se convirtió en

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

y por consiguiente también fijar el parámetro een el getErrorCodemétodo a Exception. El probleme tipo de incompatibilidad se convirtió en un poco más claro después (si no me equivoco). Sin embargo, todavía estoy dudoso acerca de la capture#xx of ? extends Exceptionya que esto significa (aún de acuerdo a mi comprensión) que el tipo de borrado no es eficaz para toda la parte del código.

¿Puede alguien me punto de salir del error en el código (y puede ser una documentación que puedo encontrar alguna explicación sobre el comportamiento interno del compilador para los genéricos, comodín y el tipo de borrado)?

Nota : el código también se queja de un tipo incompatible.

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

El resultado de esta llamada es

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

Lo siento por la longitud de esta pregunta y gracias por leer y responder a ella! Saludos

Holger:

Cuando se declara una función como Function<? extends Exception, …>, usted está diciendo que el tipo del parámetro es desconocido y, en consecuencia, no puede applyesta función ya que no sabe si el argumento actual es compatible con el tipo de parámetro desconocido. Lo mismo se aplica a la ExceptionManager<? extends Exception>, que recibe un tipo de excepción desconocida como argumento.

Esto es diferente de no saber el tipo de retorno, como cuando se devuelve una función ? extends R, que todavía saben que el resultado es asignable a Ro un tipo super R.

Existe una relación entre el argumento de entrada y el tipo de resultado que haría que este código utilizable si era genérica, sin embargo, no se puede hacer una variable (la celebración de una referencia a una Function) genérico. Puede resolver esto con el uso de métodos ordinarios, que pueden declarar los parámetros de tipo. Esto es casi recta hacia adelante, a medida que el uso excesivo de estas funciones aquí de todos modos:

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

Hay un lugar en el que una operación sin control es inevitable, cuando se devuelva un controlador específico encontrado que es adecuado a través de un instanceofcheque. Se debe tener cuidado aquí, ya que la excepción podría ser un subtipo TypeMismatchException. También es posible que la instancia es un TypeMismatchExceptionmomento de la ejecución, pero la persona que llama ha sustituido un tipo súper de ella para E. Este último es el escenario más peligroso, ya que la firma genérica sería prometen ser capaces de procesar tipos más amplios de lo que realmente puede. Siempre y cuando el método es private, usted puede fácilmente visión general de que la persona que llama (s) sólo pasan la misma instancia que se utiliza para la comprobación, por lo que va a trabajar.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=209981&siteId=1
Recomendado
Clasificación