更改 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。它可以处理所有这些(当 xString 时),还有更多要启动。

泛型和变体

泛型默认是不变的。那是一个扭曲的头部!让我们来看看:

List<String> list1 = new ArrayList<String>();
List<Object> list2 = list1; // DOES NOT COMPILE!!

这很奇怪,不是吗?但这是有道理的!毕竟,如果上面的代码有效,list1list2 都指向同一个实际列表;因此,如果您通过 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());
}