开发者

Try-catch problem with inheritance (Java)

We're trying to implement some sort of Che开发者_StackOverflow社区ss game and we have defined an abstract class Piece with constructor:

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

And an extending class could look like this:

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

The 'problem' here is, if i want to create a new King piece i have to write:

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

instead of the much simpler:

Piece king = new King(true);

So even though i simply can't create an EmptyStringException, i still have to try/catch the exception.

How can i solve this so i can still throw the EmptyStringException in Piece, but don't have to try/catch every time i need to create a new chesspiece ?


Use runtime exception:

public class EmptyStringException extends RuntimeException

instead of plain Exception. You can still document your exception in method declaration, but you are not forcing client code to deal with the exception.


Make EmptyStringException extend RuntimeException. The compile won't complain about any RuntimeExceptions thrown in a method that are missing in the throws clause.

Note that you can even include the exception in the throws clause to document that you throw it. Except for documentation purposes, this has no effect.

You should only use checked exceptions (which are derived directly from java.lang.Exception) for exceptions that the caller should handle. You should not use them for things that "can happen" like:

  • Out of memory
  • Argument errors
  • IO exceptions (which the Java RT got wrong and now serves as a perfect "how not do it" example)


Since you cannot catch exceptions from parent constructors, I'd follow the other provided suggestions and make your exception a RuntimeException, or use the existing exception IllegalArgumentException.

If you were in a situation where you couldn't modify the base class or change the exception being thrown and factory method could work.

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

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

But in your case just having Piece throw a RuntimeException is a cleaner solution.

Also if Piece's constructor is only going to be called by subclasses, consider making it protected and using assert statements to detect an empty name.

Edited - Removed incorrect advice


So even though i simply can't create an EmptyStringException, i still have to try/catch the exception.

What do you mean? Your Piece constructor is declared as throws EmptyStringException. So as far as the compiler is concerned, it can throw an EmptyStringException, simply because you've told it so.


As has been said, use a RuntimeException.

But I will add that it should be IllegalArgumentException instead of your custom one. There is no need to create your own application specific exceptions when existing standard ones exist.

Java, Class-specific Exceptions vs. Standard Exceptions


I'm reluctant to use RuntimeExceptions because this short-circuits the built-in enforcement that all excetions be handled somewhere. Yes, on your King object the exception should never happen, it's an internal coding error, so a RuntimeException is arguably appropriate. But this is clearly not the case on your Piece object.

Java's annoying rule that the super() must be the first statement prevents you from trapping the error in the King constructor.

The alternative I've used in some programs has been to move all the constructor code to an ordinary function, and then have the constructor call this function. Like:

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
    }
  }
}

In other words, avoid the need to call super. Call something else to do the initialization. Then the special rules about super don't apply.

Another option is to create a factory method for King rather than a constructor. Say:

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
    }
  }
}

If King really needs to be its own class, you could still do that above, it's the same concept.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜