Java - 在构造函数调用 super() 之前需要初始化变量

Java - Need to initialize variable before Constructor calls super()

提问人:Gonzalo Martinez 提问时间:10/19/2023 更新时间:10/19/2023 访问量:72

问:

注意:部分代码将使用西班牙语。

你好。我为我的 Spring Boot 应用程序制作了一个很好的异常模型,以便我抛出的每个个性化异常都会创建一个个性化响应(在这种情况下,它是一个称为 的类),因此我可以用 捕获该异常,并返回该特定异常创建的异常。在个性化异常中创建的每个异常的特别之处在于 a 和 a .ErrorModel@RestControllerAdvice@ExceptionHandlererrorModelerrorModelcodedescription

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(ClienteSinMorasException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  @ResponseBody
  public ErrorModel manejoDeClienteSinMoras(ClienteSinMorasException ex) {
      return ex.getErrorModel();
  }
}
@Getter
@AllArgsConstructor
public class ErrorModel {
    private final String codigo;
    private final String descripcion;
    private final ArrayList<HashMap<String, String>> errores; //don't take this into consideration
}

所以,因为每个异常都是相同的,并且工作原理相同,唯一的区别是 () 和 (),所以我创建了这个,并且它工作正常:codecodigodescriptiondescipcion

public abstract class IErrorException extends RuntimeException {
    @Getter
    private final ErrorModel errorModel;

    public abstract String getCodigo();

    public abstract String getDescripcion();

    public IErrorException() {
        super();
        this.errorModel = new ErrorModel(this.getCodigo(), this.getDescripcion(), null);
    }

    public IErrorException(ArrayList<HashMap<String, String>> errores) {
        super();
        this.errorModel = new ErrorModel(this.getCodigo(), this.getDescripcion(), errores);
    }

    public IErrorException(String errorKey, String errorValue) {
        super();
        ArrayList<HashMap<String, String>> errores = new ArrayList<>();
        errores.add(new HashMap<>(Map.of(errorKey, errorValue)));
        this.errorModel = new ErrorModel(this.getCodigo(), this.getDescripcion(), errores);
    }

}
// One of the exceptions
@Getter
public class ClienteSinMorasException extends IErrorException {

    private final String codigo = "code";
    private final String descripcion = "desc";

    public ClienteSinMorasException() {
        super();
    }

    public ClienteSinMorasException(ArrayList<HashMap<String, String>> errores) {
        super(errores);
    }

    public ClienteSinMorasException(String errorKey, String errorValue) {
        super(errorKey, errorValue);
    }

}

这是完美的(除了那些超级,但无论如何),但最近,他们要求我更改它,以便从.yml属性中读取 and,以便可以在运行时更改值而无需重新运行应用程序。这个简单的改变打破了一切。codedescription

我开始明白,这工作的唯一原因是因为在我所做的抽象类中,类型“有点已经存在”的字段(在此示例中,这返回“code”)。如果它不是也不是类型,则不起作用,它会在执行构造函数后加载所有内容,因此我最终创建了一个 .此外,必须是构造函数中的第一件事,所以我无法正确初始化变量。finalexplicit primitivethis.getCodigo()finalexplicit primitivenew ErrorModel(null, null, null)super()

// One of the exceptions
@Getter
public class ClienteSinMorasException extends IErrorException {

    private final String codigo = Yml.get("cod");
    private final String descripcion = Yml.get("desc");

    public ClienteSinMorasException() {
        super();
    }

    public ClienteSinMorasException(ArrayList<HashMap<String, String>> errores) {
        super(errores);
    }

    public ClienteSinMorasException(String errorKey, String errorValue) {
        super(errorKey, errorValue);
    }

}

这是行不通的,老实说我不知道该怎么办。

Java Spring 构造函数 Abstract-Class Super

评论

1赞 Slaw 10/19/2023
你能改变它,让它的吸气器懒惰地创建吗?ErrorModel
0赞 Anonymous 10/19/2023
两个想法。(1)如果可能的话,将这两个字段放在超类中(或者放在中间类中,如果它们有意义,则例外情况)并通过调用初始化它们:等。 (2)假设这是类中的静态方法,它是否有助于使字段和静态在自定义异常类(如)中也是如此?supersuper(Yml.get("cod"), Yml.get("desc"), errores);getYmlcodigodescriptionClienteSinMorasException
0赞 Anonymous 10/19/2023
顺便说一句,假设您不需要在创建错误模型后对其进行修改,请在任何位置声明和类似作为参数传递。还可以在构造函数中制作防御性副本,以便调用者可能进行的任何后续修改都不会伤害您。private final List<Map<String, String>> errores;
2赞 Anonymous 10/19/2023
在所有情况下,从构造函数调用可重写的方法都是一种糟糕且不鼓励的做法,因为它很容易导致像您那样的错误。因此,您应该更改设计以避免这样做。

答:

2赞 rzwitserloot 10/19/2023 #1

字段初始值设定项直到稍后才会运行。但是,您拥有的这 2 个字段似乎没有任何功能。你不需要它们。充其量,它们的存在是缓存 Yml 阅读器值的一种有点蹩脚的看法。从某种意义上说,每个异常实例都会重新创建它们,如果缓存是重点,那么在 Yml 类本身中这样做会更有效。

因此,完全摆脱这些字段。它们要么根本没有意义,要么作为缓存(即避免反复敲击磁盘以大量重新读取该 YML 文件)。如果它是缓存 - 这是错误的位置,请编辑代码以在那里添加缓存行为。无论哪种方式,对这种异常类型的正确看法是:Yml

public class ClienteSinMorasException extends IErrorException {

    @Override public String getCodigo() {
      return Yml.get("cod");
    }

    @Override public String getDescripcion() {
      return Yml.get("desc");
    }

    public ClienteSinMorasException() {
        super();
    }

    public ClienteSinMorasException(ArrayList<HashMap<String, String>> errores) {
        super(errores);
    }

    public ClienteSinMorasException(String errorKey, String errorValue) {
        super(errorKey, errorValue);
    }
}

这避免了以下问题:没有字段,因此没有字段初始值设定项,因此您不会遇到任何与初始化序列问题有关的问题。

评论

0赞 k314159 10/19/2023
基类有一个字段,该字段由其构造函数通过调用重写的 getter 进行初始化。你是说OP也应该摆脱它吗?IErrorExceptionerrorModel
0赞 rzwitserloot 10/19/2023
@k314159我没有,我也不知道为什么你会认为这个答案暗示了这一点。
0赞 k314159 10/19/2023
你说:“因此,完全摆脱田地。但是一个领域。errorModel
0赞 Gonzalo Martinez 10/19/2023
嗨,这正是我想要的!我陷入了一个棘手的境地,但这个解决方案在使其工作的同时保留了最初的想法,所以我会将其标记为公认的答案。谢谢!