One more rookie question, sorry about that.
Let's consider the following code:
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;
};
}
The following code cannot compile. It's actually complaints about a type incompatibility problem.
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
After thinking and reading about the problem, it seems that java perform a type erasure (for jvm backward compatibility) and thus the code:
private ExceptionManager<? extends Exception> defaultExceptionManager =
(exception, request, servletRequest) -> { /* ... */ }
became
private ExceptionManager<Exception> defaultExceptionManager =
(exception, request, servletRequest) -> { /* ... */ }
Which is actually fix the first parameter of the getErrorCode
(namely exception
) to Exception
.
As I understand (not sure about really understand actually), the process should be the same for the generic type. Thus
private interface ExceptionManager<D extends Exception> { /* ... */ }
should became
private interface ExceptionManager<Exception> { /* ... */ }
and consequently also fix the parameter e
in the getErrorCode
method to Exception
.
The type incompatibility probleme became a bit more clear after (if I'm right). However, I'm still doubtfully about the capture#xx of ? extends Exception
since this means (still according to my comprehension) that the Type Erasure is not effective for the whole part of code.
Can someone point me out of the error in the code (and may be a documentation which I can find some explanation about the internal behavior of the compiler for generics, wildcard and type erasure) ?
Note: the code also complains about an incompatible type.
protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex,
final HttpHeaders headers, final HttpStatus status,
final WebRequest request) {
/* ... */
int errorCode = defaultExceptionManager.getErrorCode(ex, request, servletRequest);
}
The result of this call is
Error:(154, 63) java: incompatible types:
org.springframework.beans.TypeMismatchException
cannot be converted to capture#3 of ? extends java.lang.Exception
Sorry for the length of this question and thanks for reading and answering to it! Regards