【问题标题】:Is it okay to throw exceptions from the constructor in this case? [closed]在这种情况下可以从构造函数中抛出异常吗? [关闭]
【发布时间】:2014-07-12 02:17:50
【问题描述】:

在为学校项目开发课程(如学校课程)数据库系统时,我偶然发现了一个争论的问题。

我有一个名为 Course 的课程。这是该类的构造函数(另一个是分配默认值的空白构造函数):

public Course(String name, String code, char level, int academicYear)
{
    serialNumber = nextSerialNumber++;
    if (name == null) 
    {
        throw new NullPointerException("Name can not be null.");
    }
    else
    {
        this.name = name;
    }  
    if (code == null)
    {
        throw new NullPointerException("Code can not be null.");
    }
    else
    {
        this.code = code;
    }
    if (indexOf(level, VALID_LEVEL) == -1)
    {
        throw new InvalidLevelException("Level must be one of " 
            + "characters defined in the public array in Course.");
    }
    else
    {
        this.level = level;
    }
    if (String.valueOf(academicYear).length() != NUMBER_OF_DIGITS_IN_YEAR)
    {
        throw new InvalidYearException("Year must be a four digit number!");
    }
    else
    {
        this.academicYear = academicYear;
    }
}

在哪里

InvalidLevelException

InvalidYearException

是作为子类的自定义异常

RuntimeException

我从该构造函数中抛出异常以指示是否有任何问题。例如,如果在读取数据文件时遇到错误数据,我可以拒绝它并将其写入日志(根据项目要求),只需将该构造函数放在 try-catch 块中,然后捕获这些异常,然后在那个 catch 块中记录错误数据。

在把这段代码给我的老师看之后,他说从构造函数中抛出异常是不合适的。但是,我阅读了许多鼓励这种做法的 Stackoverflow 帖子。

我的问题是:可以从上面的构造函数中抛出异常吗?

非常感谢附上答案的可靠来源(例如,官方文件或权威书籍)。

非常感谢。

【问题讨论】:

  • 我认为这主要是基于意见。不过,我不会直接抛出 NullPointerException 。我会抛出一个 IllegalArgumentException。不过,明确列出可以抛出的异常类型可能是有意义的。也 javadoc 它关于异常以及它们何时发生。一般来说,我尽量避免它,但我认为这是一个数据验证,所以应该没问题。
  • 看一下JDKHashMap类实现的代码。检查此构造函数:public HashMap(int initialCapacity, float loadFactor),其中包含:if (initialCapacity < 0) { throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); }
  • 你应该问问你的导师为什么会这样想。也许他可能有一些许多作者没有的洞察力。无论如何,如果您没有在构造函数中捕获错误,那么您将不得不在其他地方捕获它们,或者当您的对象实际需要对其进行操作时(这可能会浪费大量时间和资源早已经被避免了),或者你将不得不希望每个人都知道检查这些问题。这也不能保证。
  • 对于这种情况,您可以使用CourseBuilder 类,该类将获取必要的数据来创建Course 并在build() 方法中抛出异常,因此可以将异常从Course 构造函数并位于 build() 方法中。但这会导致简单而优雅的设计过于复杂。你应该告诉你的教授访问这个网站,可能会发布一个问题:)。

标签: java exception constructor


【解决方案1】:

我看不出在构造函数中抛出异常有什么问题。这意味着无法在有效状态下创建对象。

您个人抛出正确的异常,但如果您可以使用 Java 异常,请不要使用自定义异常 InvalidLevelExceptionInvalidYearException 应替换为 IllegalArgumentExceptionNullPointerException 是正确的异常,如果参数为 null .

我要改变的另一件事是风格:检查你的论点然后做其他所有事情。

public Course(String name, String code, char level, int academicYear)
{
    if (name == null) {
        throw new NullPointerException("Name can not be null.");
    }
    if (code == null) {
        throw new NullPointerException("Code can not be null.");
    }

    if (indexOf(level, VALID_LEVEL) == -1) {
        throw new InvalidLevelException("Level must be one of " 
            + "characters defined in the public array in Course.");
    }

    if (String.valueOf(academicYear).length() != NUMBER_OF_DIGITS_IN_YEAR) {
        throw new InvalidYearException("Year must be a four digit number!");
    }

    serialNumber = nextSerialNumber++;
    this.code = code;
    this.academicYear = academicYear;
    this.level = level;
    this.name = name;
}

(ps如果无法创建对象,为什么要增加序列号?)

很优雅吧? -- 另一件事是使消息更具体。

无论如何,我认为最好的来源是整个 JDK 平台,因为在构造函数中抛出异常是一种常见的模式。


正如 Luiggi Mendoza 在 cmets 中所说,如果您需要一位教授为您的老师,请联系 HashMap constructor

187     public More ...HashMap(int initialCapacity, float loadFactor) {
188         if (initialCapacity < 0)
189             throw new IllegalArgumentException("Illegal initial capacity: " +
190                                                initialCapacity);
191         if (initialCapacity > MAXIMUM_CAPACITY)
192             initialCapacity = MAXIMUM_CAPACITY;
193         if (loadFactor <= 0 || Float.isNaN(loadFactor))
194             throw new IllegalArgumentException("Illegal load factor: " +
195                                                loadFactor);
196 
197         // Find a power of 2 >= initialCapacity
198         int capacity = 1;
199         while (capacity < initialCapacity)
200             capacity <<= 1;
201 
202         this.loadFactor = loadFactor;
203         threshold = (int)(capacity * loadFactor);
204         table = new Entry[capacity];
205         init();
206     }

【讨论】:

    【解决方案2】:

    上面的构造函数可以抛出异常吗?

    您的问题涉及Exception 处理的一个方面。另一方面是准确描述问题。我将简要介绍两者。

    代码失败的地方是否抛出异常?

    原因很简单。您希望用户确切地知道问题发生在哪里,并且如果您将异常传播到整个地方,那么您将使您的代码用户更难破译问题所在。如果问题出在对象的构造函数中,您就知道问题发生在该对象的创建过程中。

    这为用户提供了一个重要的线索,这是你所能做的。您不知道用户将如何使用您的代码,因此您需要让他们尽可能容易地破译他们问题的来源。 p>

    异常消息是否描述了问题?

    使用适当类型的Exception 很重要。如果他们提供了无效参数,则抛出IllegalArgumentException。如果他们提供了一个空值,则抛出一个NullPointerExceptionException 应该尽可能有效地描述问题。

    第二部分是您附加的消息。我看过的次数:

    Exception: Exception Occurred
    

    高得令人沮丧。它一点帮助也没有,而且是处理代码的一种极其懒惰的方式。你需要说清楚:

    NullPointerException: Parameter X was null
    

    用户立即知道他们为X 传递的值是null。简而言之,确保消息具体描述了导致它的问题。

    补充阅读

    • 有关此主题的更多详细信息,请查看this link,了解有关异常处理的最佳实践。这将有助于解决您遇到的任何其他问题。

    【讨论】:

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