【问题标题】:Java try catch in constructorJava尝试在构造函数中捕获
【发布时间】:2020-06-19 13:34:58
【问题描述】:

我写了这个返回财政年度的类:

public class Fiscal {

private Calendar calendar;

public FiscalDate(String date) {
    try {
        DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Date formattedDate = formatter.parse(date);
        this.calendarDate = Calendar.getInstance();
        this.calendarDate.setTime(formattedDate);
    }
    catch (ParseException e) {
        System.out.print(e);
    }
}
}

我是这样使用它的:

String test = new Fiscal("2020-03-31").display();

一切正常,我只是想知道我在构造函数中的 try 和 catch 是否有任何问题?有什么方法可以改进这门课吗?

【问题讨论】:

  • 不会捕获任何异常。让来电者处理。否则,你会返回一个未初始化的对象吗?
  • 为什么会有错呢?
  • 不幸的是,由于 java.util 日期时间的东西都已经过时了,最好的办法就是把大部分东西扔掉。但你是对的,构造函数不应该有这段代码,让其他代码处理转换并将结果传递给构造函数。
  • 约翰尼莫普是正确的。不要捕获任何异常。有了catch,如果字符串参数无效,构造函数将创建一个带有空日历的 FiscalDate 实例。那是你要的吗?您是否希望程序中的 FiscalDate 对象的 getFiscalYear 方法将因神秘的 NullPointerExceptions 而失败?
  • 除其他建议外,还将calendarDate字段标记为final。如果你一开始就这样做了,编译器将不允许你产生异常吞咽的 catch 块。

标签: java


【解决方案1】:

是的,就像@Johnny Mopp 在 cmets 中所说,您不应该默默地捕获异常,而是抛出异常并让实现该类的人选择如何处理它。

public FiscalDate(String date) throws ParseException {
    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    Date formattedDate = formatter.parse(date);
    calendarDate = Calendar.getInstance();
    calendarDate.setTime(formattedDate);
}
try {
    FiscalDate date = new FiscalDate("my date string");

    // some code that utilizes date
} catch (ParseException exception) {
    // darn, something went wrong, time to handle it!
}

【讨论】:

  • 我建议先用Date 初始化类,让解析和模型完全分开。
【解决方案2】:

我反对构造函数中的任何代码。您可以遵循本文中的指南:https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html

基本上用字符串表示初始化您的对象并仅在#displayFiscalYear 进行解析。任何其他行为都可以通过装饰器实现,例如缓存以避免重复计算。

它可能看起来过于复杂,购买恕我直言,这是最好的方法。如果这不能说服你,我会采用工厂方法。在工厂方法中计算解析,构造函数直接用 Calendar 对象初始化。

附:如果可以的话,我还建议迁移到 java 8 的 java.time。

【讨论】:

  • 从您的链接中引用:这是一个坏主意,原因之一是:它会阻止对象的组合并使它们不可扩展。在问题中的情况下,我认为没有害处完成组合和扩展。我主要希望为已经获得 LocalDate 的调用者看到一个 FiscalDate(LocalDate) 构造函数(推荐用于日期),但是为那些拥有字符串的人添加一个更方便的构造函数很好。另一方面,将日期作为字符串保存在对象中是不好的。
【解决方案3】:
  1. 使用来自 java.time(现代 Java 日期和时间 API)中的 LocalDate 作为日期。
  2. 在构造函数中捕获异常通常是不好的。如果您仍然可以将对象初始化为完全有效且有意义的状态,或者您希望将异常包装到您抛出给调用者的不同异常中,则可以这样做。

java.time

public class FiscalDate {

    private LocalDate calendarDate;

}

LocalDate 是预测公历中没有时间的日期。 Calendar 是具有时区、周编号方案等的日期和时间,理论上在任何日历系统中。 Calendar 类的设计也很糟糕,而且早已过时。所以我推荐LocalDate

构造函数

现在我们想要哪些构造函数?我发现有一个接受LocalDate 的构造函数是很自然的:

    public FiscalDate(LocalDate calendarDate) {
        this.calendarDate = calendarDate;
    }

如果您预见到调用者有时会将日期作为字符串而不是LocalDate,那么提供一个接受字符串的便捷构造函数是很好的:

    /** @throws DateTimeParseException if dateString is not a valid date string in format yyyy-MM-dd */
    public FiscalDate(String dateString) {
        this(LocalDate.parse(dateString));
    }

this() 调用调用之前的构造函数。它类似于super() 调用,只是它不是调用超类中的构造函数,而是调用同一个类中的构造函数。如果字符串无法解析为有效日期,LocalDate.parse() 将抛出 DateTimeParseException。这是一个未经检查的异常,所以我们不需要声明构造函数可能会抛出它,但调用者知道它很高兴,所以我们将它放在 Javadoc 注释中。

我相信决定在无效字符串日期的情况下做什么应该是调用者的工作,而不是这个类。所以抛出异常是合适的(因为它是或包装在更合适的异常类型中)。如果要在构造函数中捕获异常,则需要初始化对象,而我看不出您将如何以任何有意义的方式做到这一点。

链接

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    相关资源
    最近更新 更多