Thursday, October 4, 2012

Clean Code by Example: Throwing Exceptions in a Java Constructor

In the last post I weighted the utility of checked exceptions against unchecked exceptions. In this article I will investigate the perils of throwing exception from a constructor. As previously, I will try to stick to the guidelines given in Robert C. Martins «Clean Code». But since Joshua Bloch's «Effective Java» contributes much more to the topic of error handling than «Uncle Bob», it will be the authoritative source for this post. You can find the program code discussed here in this Github repository.

Exceptions thrown by a constructor

The architect reviewing my bowling example did not quiet like that my BowlingGame constructor threw an (checked) exception. My argument went, that it was there for mere convenience, to ease testing. With its help bowling games could be rolled wholesale, like so:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = new BowlingGame(new int[] { 1, 2, 3, 4, 5, 1, 2, 3,
      4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());
}

He countered, this could also be achieved by a normal method. And right he is! Additionally, with such a method, rolls could be added to a game in several bulks. So lets re-factor the constructor from:

public BowlingGame(int[] rolls) throws BowlingException {
  for (int roll : rolls) {
    addRoll(roll);
  }
}

into a «normal method» which the constructor calls:

public BowlingGame(int[] rolls) {
  addRolls(rolls);
}

public void addRolls(int[] rolls) {
  for (int roll : rolls) {
    addRoll(roll);
  }
}


Well, this some sleight of hand in that change. Where did the «throws BowlingException» declaration go? It already went during the last post in favor of an unchecked exception! The declaration is no longer required!

Notice something with the constructor? I did not, when I initially wrote this piece of code: the constructor called—and still calls—a non-final method! This is generally considered bad practice. If BowlingGame was sub-classed and addRoll/addRolls was overridden, then the constructor would call the sub-classes implementation although the sub-class constructor would have not been executed yet. That means the instance might not be in a usable state [Bloch, p. 89]! That makes one argument against using complex initialization within constructors: the temptation to call virtual methods.

As to further arguments against throwing exceptions in constructors—both Uncle Bob and Mr. Bloch give non. Most of the community on Stack Overflow seem to have no strong feelings against throwing exceptions from within constructors. Author and Stack Overflow veteran Jon Skeet, however, names two instances where one must be wary. The first is that the object under construction might escape immediate destruction and garbage collection:
It's possible for the «half-constructed» object to stick around though, if it's made itself visible earlier in the constructor (e.g. by assigning a static field, or adding itself to a collection).
—Jon Skeet, «Can constructors throw exceptions in Java?», Stack Overflow
The second pitfall is less exotic. It is related to resources that require clean-up:
Because the caller (usually) will have no way of using the new object, the constructor ought to be careful to avoid acquiring unmanaged resources (file handles etc) and then throwing an exception without releasing them. For example, if the constructor tries to open a FileInputStream and a FileOutputStream, and the first succeeds but the second fails, you should try to close the first stream. This becomes harder if it's a subclass constructor which throws the exception…
—Jon Skeet, «Can constructors throw exceptions in Java?», Stack Overflow
Both issues do not apply to the bowling constructor. The problem of calling a virtual method however must be fixed! There are various options: ditch the constructor altogether and call addRolls(int[]) instead on a freshly created instance. Alternatively, mark BowlingGame final thus preventing any sub-classes that could override addRolls(int[]). A little less drastic would be to just mark addRolls(int[]) and all the methods it calls recursively final`, which achieves the same effect. Or we can go for a static factory method:

public static BowlingGame fromRolls(int[] rolls) {
  BowlingGame game = new BowlingGame();
  game.addRolls(rolls);
  return game;
}

Apart from being permitted to call virtual methods, it also improves readability at the call site. In the following code the purpose of the int array is not immediately clear:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = new BowlingGame(new int[] { 1, 2, 3, 4, 5, 1, 2, 3,
      4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());
}

Replaced by a static factory method, it is clear:

@Test
public void neitherStrikeNorSpare() {
  BowlingGame game = BowlingGame.fromRolls(new int[] { 1, 2, 3, 4, 5, 1,
      2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 });
  assertEquals(60, game.getScore());

This is clearly the best way to go, in this case.
One questions remains: what about unit-tests for the newly created addRolls(int[]) method? I guess we are somewhat absolved from this obligation since the static factory method (and before that, the constructor) are already quiet thoroughly tested. If we opted to also test addRolls(int[]) the tests would be almost duplicated, with only a method call renamed. I do not know yet, if there is a pattern to efficiently write such test-cases.

Conclusion

There is nothing principally bad about throwing exceptions from a constructor. The danger of zombie objects or unreleased resources advise more against doing overly complicated stuff inside constructors as against throwing exceptions. So does the danger of accidentally calling virtual methods from the constructor that may easily break the object.

7 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This is quite educational arrange. It has famous breeding about what I rarity to vouch. Colossal proverb. This trumpet is a famous tone to nab to troths. Congratulations on a career well achieved. This arrange is synchronous s informative impolite festivity to pity. I appreciated what you ok extremely here.

    Data Science course in Chennai | Data science course in bangalore
    Data science course in pune | Data science online course
    Python course in Kalyan nagar

    ReplyDelete
  3. Great thoughts you got there, believe I may possibly try just some of it throughout my daily life.
    java training in chennai | java training in electronic city

    java training in marathahalli | java training in btm layout

    ReplyDelete
  4. Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.

    AWS Training in Bangalore | Amazon Web Services Training in bangalore
    AWS Training in pune | Amazon Web Services Training in Pune, india
    AWS Training in Chennai|Amazon Web Services Training in Chennai,India
    Aws online training and certification | amazon web services online training ,india

    ReplyDelete
  5. Impressive blog
    Apponix Technologies
    www.apponix.com

    ReplyDelete

  6. Expected to form you an almost no word to thank you once more with respect to the decent recommendations you've contributed here.
    Machine Learning Training In Hyderabad

    Machine Learning Course In Hyderabad

    ReplyDelete
  7. I wanted to leave a little comment to support you and wish you a good continuation. Wishing you the best of luck for all your blogging efforts.
    data analytics training in hyderabad

    ReplyDelete