问题如下:
有一个ErrorMessageBuilder,实现这个接口的类的示例可以根据传递给它们的异常生成消息,此外,还有一个选项来检查类是否可以处理传递的异常。
interface ErrorMessageBuilder<T extends Exception> {
String buildMessage(T exception);
boolean canHandle(Class<? extends Exception> clazz);
}
接下来,有一个类ExceptionHandlerPair,用于创建对(Exception+Handler)
class ExceptionHandlerPair<T extends Exception>{
private final Class<T> clazz;
private final ErrorMessageBuilder<? extends Exception> builder;
public ExceptionHandlerPair(Class<T> clazz, ErrorMessageBuilder<? extends Exception> builder) {
if(!builder.canHandle(clazz)){
throw new IllegalArgumentException("Handler can't handle this exception");
}
this.clazz = clazz;
this.builder = builder;
}
public boolean canHandle(Class<? extends Exception> clazz) {
return this.clazz.isAssignableFrom(clazz);
}
public ErrorMessageBuilder<? extends Exception> getBuilder() {
return builder;
}
}
之后,我尝试创建一个Bilder并创建对。例如,我创建了一个RuntimeExceptionErrorMessageBuilder,它可以处理任何RuntimeException:
class RuntimeExceptionErrorMessageBuilder implements ErrorMessageBuilder<RuntimeException> {
public String buildMessage(RuntimeException exception) {
return "Some runtime exception";
}
@Override
public boolean canHandle(Class<? extends Exception> clazz) {
return RuntimeException.class.isAssignableFrom(clazz);
}
}
public class SomeClass {
public static void main(String[] args) {
var builder = new RuntimeExceptionErrorMessageBuilder();
// builder.canHandle(NullPointerException.class); // ? -> true
// builder.canHandle(IllegalArgumentException.class); // ? -> true
// builder.canHandle(Exception.class); // ? -> false
var pair = new ExceptionHandlerPair<>(NullPointerException.class, builder);
if(pair.canHandle(NullPointerException.class)){ // ? -> true
var message = pair.getBuilder().buildMessage(new NullPointerException()); // <- error Required type: capture of ? extends Exception, Provided: IllegalArgumentException
System.out.println(message);
}
if(pair.canHandle(IllegalArgumentException.class)){ // ? -> true
var message = pair.getBuilder().buildMessage(new IllegalArgumentException()); // <- error Required type: capture of ? extends Exception, Provided: IllegalArgumentException
System.out.println(message);
}
}
}
我们得到一个错误,buildMessage(x)参数给出了一个错误,x参数应该是Exception的后代,但IllegalArgumentException是后代,这是什么问题?
好吧,我想如果这个限制适用于参数,那么让我们添加一个ExceptionWrapper,如下所示:
class ErrorWrapper<T extends Exception>{
private final T exception;
public ErrorWrapper(T exception) {
this.exception = exception;
}
public T getException() {
return exception;
}
}
之后,使用ErrorWrapper修改ErrorMessageBuilder和RuntimeExceptionErrorMessageBuilder:
interface ErrorMessageBuilder<T extends Exception> {
String buildMessage(ErrorWrapper<T> exception);
boolean canHandle(Class<? extends Exception> clazz);
}
class RuntimeExceptionErrorMessageBuilder implements ErrorMessageBuilder<RuntimeException> {
public String buildMessage(ErrorWrapper<RuntimeException> exception) {
return "Some runtime exception";
}
@Override
public boolean canHandle(Class<? extends Exception> clazz) {
return RuntimeException.class.isAssignableFrom(clazz);
}
}
然后更改main并尝试创建具有更改的消息,但即使我尝试欺骗java也无法工作:错误所需类型:ErrorWrapper〈捕获?扩展异常〉,提供:ErrorWrapper〈capture of?extends Exception〉
public class SomeClassWrapper {
public static void main(String[] args) {
var builder = new RuntimeExceptionErrorMessageBuilder();
ExceptionHandlerPair<NullPointerException> pair = new ExceptionHandlerPair<>(NullPointerException.class, builder);
if(pair.canHandle(NullPointerException.class)){ // ? -> true
ErrorWrapper<NullPointerException> npeWrapper = new ErrorWrapper<>(new NullPointerException());
var message = pair.getBuilder().buildMessage(npeWrapper); // <- error Required type: ErrorWrapper <capture of ? extends Exception>, Provided: ErrorWrapper<NullPointerException
System.out.println(message);
}
//
if(pair.canHandle(NullPointerException.class)){ // ? -> true
// and here we'll try to swoon over Java!!! :)
ErrorWrapper<? extends Exception> npeWrapper = new ErrorWrapper<>(new NullPointerException());
var message = pair.getBuilder().buildMessage(npeWrapper); // <- error Required type: ErrorWrapper <capture of ? extends Exception>, Provided: ErrorWrapper <capture of ? extends Exception>
System.out.println(message);
}
}
}
如果有人能解释这种行为,我将不胜感激!!!提前感谢!
2条答案
按热度按时间bfrts1fy1#
在我看来,修复方法是在
ExceptionHandlerPair
类中引入另一个泛型类型变量。在下面的代码中,类型变量T
是ErrorMessageBuilder
处理的异常类型,U
是canHandle
方法将返回true
的异常类型:我对你的代码做了这个修改,你的
SomeClass
类被编译了。使用通配符的问题(
?
)是你扔掉了关于构建器处理什么类型的异常的信息。在你的代码中,pair.getBuilder()
总是返回ErrorMessageBuilder<? extends Exception>
,不管你的pair
是如何构建的。这个构建器处理的异常类型将是Exception
的任意子类,但是你不知道是什么。它可能是一个ErrorMessageBuilder<Exception>
,或者一个ErrorMessageBuilder<RuntimeException>
,如果是这种情况,你可以将一个NullPointerException
传递给.getBuilder()
方法。但是,无限通配符也允许getBuilder()
方法返回一个ErrorMessageBuilder<NegativeArraySizeException>
,例如,你不能把NullPointerException
传递给buildMessage()
方法。91zkwejq2#
导致此错误的原因是您在以下方法
ExceptionHandlerPair.getBuilder()
中使用通配符作为返回类型,不建议这样做,因为它会导致编译器产生歧义您可以查看下面的讨论,了解有关不建议这样做的原因的更多详细信息Generic wildcard types should not be used in return parametershttps://rules.sonarsource.com/java/RSPEC-1452
强烈建议不要使用通配符类型作为返回类型。因为类型推断规则相当复杂,API的用户不太可能知道如何正确使用它。让我们以返回“List〈?extends Animal〉"的方法为例。是否有可能在这个列表中添加一个狗,猫......我们根本不知道。编译器也不知道。这就是为什么它不允许这样直接使用。通配符类型的使用应该限于方法参数。当方法返回通配符类型时,此规则会引起问题。
相反,您只需将getBuilder()的返回类型更改为
ErrorMessageBuilder<T>
}