继承中的try-catch问题(Java)

3

我们正在尝试实现某种国际象棋游戏,并定义了一个带有构造函数的抽象类 Piece:

public Piece(String name) throws EmptyStringException{
    if(name.length()<=0)
        throw new EmptyStringException();
    pieceName = name;
}

一个扩展类可能是这样的:

public King(boolean white) throws EmptyStringException{         
    super("King", white);        
}

这里的“问题”是,如果我想创建一个新的国王棋子,我必须编写以下代码:

try {
    Piece king = new King(true);
} catch(EmptyStringException e){
    e.printStackTrace();
}

相比较于更为简单的:

Piece king = new King(true);

即使我无法创建EmptyStringException,但我仍然需要尝试/捕获异常。

如何解决这个问题,以便我仍然可以在Piece中抛出EmptyStringException,但不必每次需要创建新的棋子时都要尝试/捕获?

6个回答

7

使用运行时异常:

public class EmptyStringException extends RuntimeException

与其使用普通的Exception,你可以在方法声明中记录异常信息,但是不会强制客户端代码处理异常。


2
在JavaDoc中可以并且应该记录 @throws EmptyStringException if {@code name} is empty - pickypg

1

由于您无法从父构造函数中捕获异常,因此我建议遵循其他提供的建议,使您的异常成为RuntimeException或使用现有的异常IllegalArgumentException。

如果您处于无法修改基类或更改抛出的异常并且工厂方法可以解决问题的情况下,则可以采用该方法。

public class King {
   private King() {
      super("King");
   }

   public King createInstance() {
      try {
         new King();
      } catch (EmptyStringException e) {
         throw new RuntimeException("Unexpected expection thrown", e);
      }
   }
}

但在你的情况下,让Piece抛出一个RuntimeException是一个更清晰的解决方案。

另外,如果Piece的构造函数只会被子类调用,请考虑将其设置为protected,并使用assert语句来检测空名称。

编辑-删除不正确的建议


2
我尝试修改King的构造函数,但实际上给了我一个错误,说super应该是构造函数中要做的第一件事。也许我还是做错了什么... 另外,感谢提到将其设置为protected。 - Aerus
在这种情况下,我会使用assert或IllegalArgumentException。 - Michael Krussel
修改了帖子以处理无法从super()捕获异常的事实。 - Michael Krussel

1

EmptyStringException 扩展 RuntimeException。编译器不会抱怨在方法中抛出的任何RuntimeException,这些异常在throws子句中丢失。

请注意,您甚至可以在throws子句中包含异常以记录您抛出它。除了文档目的之外,这没有任何影响。

您应该仅使用检查异常(直接派生自java.lang.Exception)处理调用方应该处理的异常。您不应将它们用于可能发生的事情,例如:

  • 内存不足
  • 参数错误
  • IO异常(Java RT做错了现在是一个完美的“如何不做”的例子)

谢谢您详细讲解何时应该使用已检查异常,这部分异常需要一些更新 :). - Aerus

0

我不太愿意使用RuntimeExceptions,因为这会短路内置的强制要求所有异常都必须在某个地方处理的规则。是的,在您的King对象上,异常永远不应该发生,这是一个内部编码错误,因此RuntimeException可能是合适的选择。但是在您的Piece对象上显然不是这种情况。

Java令人讨厌的规则是super()必须是第一条语句,这使您无法在King构造函数中捕获错误。

我在一些程序中使用的替代方法是将所有构造函数代码移动到普通函数中,然后让构造函数调用此函数。像这样:

public class Piece
{
  public Piece(String name) throws EmptyStringException
  {
    initPiece(name);
  }
  // Protect so outsiders must use legal constructors
  protected Piece()
  {
    // nop
  }
  protected void initPiece(String name) throws EmptyStringException
  {
    if (name.length()==0)
      throw new EmptyStringException();
    pieceName=name;
  }
}
public class King extends Piece
{
  public King(boolean white)
  {
    try
    {
      initPiece("King");
    }
    catch (EmptyStringException panic)
    {
      throw new RuntimeException(panic.toString()); // should never happen
    }
  }
}

换句话说,避免调用super的需要。调用其他方法来进行初始化。这样特殊规则就不适用于super了。
另一个选择是为King创建一个工厂方法而不是构造函数。比如说:
public class Piece
{
  public Piece(String name) throws EmptyStringException
  {
    if (name.length()==0)
      throw new EmptyStringException();
    pieceName=name;
  }
  public Piece makeKing(boolean white)
  {
    try
    {
      return new Piece("King");
    }
    catch (EmptyStringException e)
    {
      throw new RuntimeException(e.toString()); // won't ever happen
    }
  }
}

如果King确实需要成为自己的类,你仍然可以在上面执行此操作,这是相同的概念。

0
所以即使我根本无法创建EmptyStringException,我仍然必须尝试/捕获异常。 你是什么意思?你的Piece构造函数声明为throws EmptyStringException。因此,就编译器而言,它可以抛出EmptyStringException,只是因为你已经告诉了它。

0
如前所述,请使用RuntimeException。
但我要补充说,应该使用IllegalArgumentException而不是自定义异常。存在标准异常时,没有必要创建自己的应用程序特定异常。 Java,类特定异常与标准异常

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接