更改 Return 类型的重写方法?
Change Return type of Overridden Methods?
下面是“ControllerAdvice”,它充当多个微服务的全局异常处理程序。
// Custom Exception Handler
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}
@ExceptionHandler(value = Exception.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse globalException(Exception ex) {
return new ErrorMessage("INTERNAL_SERVER_ERROR", "500");
}
现在,对于我的一个微服务,我需要自定义来自上述异常处理程序的错误响应,即我需要将来自全局异常处理程序的“ErrorResponse”包装在另一个名为“ErrorResponseWrapper”的对象下
class ErrorResponseWrapper extends ErrorResponse {
String type;
// getter & setters
}
现在在我的微服务中我需要自定义错误响应,我只是重写了 GlobalExceptionHandler 方法
@RestControllerAdvice
public class ControllerException extends GlobalExceptionHandler {
// This Works FINE...
@Override
public ErrorResponseWrapper globalException(Exception ex) {
//
ErrorResponse error = super.globalException(ex);
// New Response Wrapper
ErrorResponseWrapper wrapper = new ResponseWrapper();
wrapper.setType("SOMETHING");
return wrapper;
}
//Issue is here:-
// I get Compilation Error: ReturnType is Not Compatible with the GlobalException return type.
@Override
public ResponseEntity<ErrorResponseWrapper> customException(CustomException ex) {
ErrorResponse error = super.customException(ex).getBody();
// New Response Wrapper
ErrorResponseWrapper wrapper = new ResponseWrapper();
wrapper.setType("SOMETHING");
return new ResponseEntity<>(wrapper,ex.getStatus());
}
}
它与 First Overridden 方法一起工作正常,为什么它与 ResponseEntity 不同?我做错了什么,或者我该如何实现?
在原来的 customException
方法中尝试以下操作:
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<T extends ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}
你的问题是泛型,默认情况下它们是不变的,这会绊倒很多人并且可能不会立即认为是合乎逻辑或正确的(但是,它是 - 继续阅读)。
有这3个概念,叫做invariant / covariant / contravariant.
协变
协变意味着事物的子类型与事物一样好。
Java 基本类型是协变的。这里:
Integer x = 5;
Number n = x; // This compiles and runs just great.
请注意 x
的类型是 Integer,它是 Number
的 子类型 ,但这在这里没问题。那是因为这里应用了协方差。
逆变
逆变是相反的:超类型是一个公平的标准。这实际上是 java 的一部分:重写方法时,您可以对参数使用超类型,这很好。这编译:
public class Parent {
public void foo(String x) {}
}
public class Child extends Parent {
@Override public void foo(Object x) {}
}
这样做的原因应该很明显:因为常识:所有字符串也是对象,因此 Child 定义的 foo
方法可以处理所有可能的调用 foo
的情况Child
被视为 Parent
。它可以处理所有这些(当 x
是 String
时),还有更多要启动。
泛型和变体
泛型默认是不变的。那是一个扭曲的头部!让我们来看看:
List<String> list1 = new ArrayList<String>();
List<Object> list2 = list1; // DOES NOT COMPILE!!
这很奇怪,不是吗?但这是有道理的!毕竟,如果上面的代码有效,list1
和 list2
都指向同一个实际列表;因此,如果您通过 list1
ref 更改此列表,您将能够在使用 list2
ref 时看到更改。但是,问题就在这里:我可以 运行 list2.add(new Object())
,但这意味着 list1.get()
return 是 Object
而不是 String
.
这就是泛型不变的原因:因为宇宙就是这样运作的。
这适用于任何地方。因此,我们在这里找到了问题的症结所在:
ResponseEntity<ErrorResponseWrapper>
不是 ResponseEntity<ErrorResponse>
!
的子类型
幸运的是,泛型非常灵活。如果需要,您可以拥有协变甚至逆变;只是,求它。在这里,现在可以正常工作了:
public class Parent {
public ResponseEntity<? extends ErrorResponse> foo() {}
}
public class Child extends Parent {
@Override public ResponseEntity<? extends ErrorResponseWrapper> foo() {}
}
确实可以。 ? extends
是 java-ese 用于:我想要泛型,但协变。然后编译器会帮助你,防止你破坏世界。让我们试试看:
List<String> list1 = new ArrayList<>();
List<? extends Object> list2 = list1; // now it compiles!
list2.add(new Object()); // but this does not!
在任何 List<? extends Y>
上调用 .add()
,其中 Y 可以是您喜欢的任何东西 不会编译 ,除非您传递文字 null
和只是因为 null
是每种类型。这是故意的 - 它允许您将该字符串列表分配给 ref.
修复
返回原始类型并更新 return 类型。如果你做不到,这里也无能为力。
定义 CustomHandler 时需要使用
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<? extends ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}
下面是“ControllerAdvice”,它充当多个微服务的全局异常处理程序。
// Custom Exception Handler
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}
@ExceptionHandler(value = Exception.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse globalException(Exception ex) {
return new ErrorMessage("INTERNAL_SERVER_ERROR", "500");
}
现在,对于我的一个微服务,我需要自定义来自上述异常处理程序的错误响应,即我需要将来自全局异常处理程序的“ErrorResponse”包装在另一个名为“ErrorResponseWrapper”的对象下
class ErrorResponseWrapper extends ErrorResponse {
String type;
// getter & setters
}
现在在我的微服务中我需要自定义错误响应,我只是重写了 GlobalExceptionHandler 方法
@RestControllerAdvice
public class ControllerException extends GlobalExceptionHandler {
// This Works FINE...
@Override
public ErrorResponseWrapper globalException(Exception ex) {
//
ErrorResponse error = super.globalException(ex);
// New Response Wrapper
ErrorResponseWrapper wrapper = new ResponseWrapper();
wrapper.setType("SOMETHING");
return wrapper;
}
//Issue is here:-
// I get Compilation Error: ReturnType is Not Compatible with the GlobalException return type.
@Override
public ResponseEntity<ErrorResponseWrapper> customException(CustomException ex) {
ErrorResponse error = super.customException(ex).getBody();
// New Response Wrapper
ErrorResponseWrapper wrapper = new ResponseWrapper();
wrapper.setType("SOMETHING");
return new ResponseEntity<>(wrapper,ex.getStatus());
}
}
它与 First Overridden 方法一起工作正常,为什么它与 ResponseEntity 不同?我做错了什么,或者我该如何实现?
在原来的 customException
方法中尝试以下操作:
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<T extends ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}
你的问题是泛型,默认情况下它们是不变的,这会绊倒很多人并且可能不会立即认为是合乎逻辑或正确的(但是,它是 - 继续阅读)。
有这3个概念,叫做invariant / covariant / contravariant.
协变
协变意味着事物的子类型与事物一样好。
Java 基本类型是协变的。这里:
Integer x = 5;
Number n = x; // This compiles and runs just great.
请注意 x
的类型是 Integer,它是 Number
的 子类型 ,但这在这里没问题。那是因为这里应用了协方差。
逆变
逆变是相反的:超类型是一个公平的标准。这实际上是 java 的一部分:重写方法时,您可以对参数使用超类型,这很好。这编译:
public class Parent {
public void foo(String x) {}
}
public class Child extends Parent {
@Override public void foo(Object x) {}
}
这样做的原因应该很明显:因为常识:所有字符串也是对象,因此 Child 定义的 foo
方法可以处理所有可能的调用 foo
的情况Child
被视为 Parent
。它可以处理所有这些(当 x
是 String
时),还有更多要启动。
泛型和变体
泛型默认是不变的。那是一个扭曲的头部!让我们来看看:
List<String> list1 = new ArrayList<String>();
List<Object> list2 = list1; // DOES NOT COMPILE!!
这很奇怪,不是吗?但这是有道理的!毕竟,如果上面的代码有效,list1
和 list2
都指向同一个实际列表;因此,如果您通过 list1
ref 更改此列表,您将能够在使用 list2
ref 时看到更改。但是,问题就在这里:我可以 运行 list2.add(new Object())
,但这意味着 list1.get()
return 是 Object
而不是 String
.
这就是泛型不变的原因:因为宇宙就是这样运作的。
这适用于任何地方。因此,我们在这里找到了问题的症结所在:
ResponseEntity<ErrorResponseWrapper>
不是 ResponseEntity<ErrorResponse>
!
幸运的是,泛型非常灵活。如果需要,您可以拥有协变甚至逆变;只是,求它。在这里,现在可以正常工作了:
public class Parent {
public ResponseEntity<? extends ErrorResponse> foo() {}
}
public class Child extends Parent {
@Override public ResponseEntity<? extends ErrorResponseWrapper> foo() {}
}
确实可以。 ? extends
是 java-ese 用于:我想要泛型,但协变。然后编译器会帮助你,防止你破坏世界。让我们试试看:
List<String> list1 = new ArrayList<>();
List<? extends Object> list2 = list1; // now it compiles!
list2.add(new Object()); // but this does not!
在任何 List<? extends Y>
上调用 .add()
,其中 Y 可以是您喜欢的任何东西 不会编译 ,除非您传递文字 null
和只是因为 null
是每种类型。这是故意的 - 它允许您将该字符串列表分配给 ref.
修复
返回原始类型并更新 return 类型。如果你做不到,这里也无能为力。
定义 CustomHandler 时需要使用
@ExceptionHandler(value = CustomException.class)
public ResponseEntity<? extends ErrorResponse> customException(CustomException ex) {
ErrorResponse error = new ErrorResponse(ex.getErrorDescription(), ex.getErrorMessage());
return new ResponseEntity<>(error, ex.getStatus());
}