【发布时间】:2018-08-19 07:58:38
【问题描述】:
还有一个菜鸟问题,抱歉。
让我们考虑以下代码:
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 持怀疑态度,因为这意味着(仍然根据我的理解)类型擦除对整个代码部分无效。
有人能指出代码中的错误吗(可能是一个文档,我可以找到一些关于泛型、通配符和类型擦除的编译器内部行为的解释)? em>
注意:代码还抱怨类型不兼容。
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
抱歉这个问题太长了,感谢您阅读和回答! 问候
【问题讨论】:
-
我认为这里的第一个答案描述了如何找出问题所在:stackoverflow.com/questions/31227149/…
-
如果在分配之前显式转换 lambda 会发生什么?
ExceptionManager<? extends Exception> defaultExceptionManager = (ExceptionManager<? extends Exception>) ((exception, request, servletRequest) -> { ... )?而且,顺便说一句,一个简单的ExceptionManager<Exception>就不能完成这项工作吗? -
感谢@JohnB 的两个回复(您指出的帖子很有趣,之前没有发表过)。对于第二点,演员表不会改变行为。对于第二点,我希望能够在接口中保持通用性,这样我就可以编写管理器(例如:
ExceptionManager<TypeMismatchException> tmeManager),而无需在实现中强制转换任何内容。注意代码的结构主要是为了学习如何处理java8的一些特性(函数、lambda和泛型)。再次感谢您的时间和回答。
标签: java generics java-8 wildcard