Running on Java 24-ea+24-2960 (Preview)
Home of The JavaSpecialists' Newsletter

120Exceptions From Constructors

Author: Dr. Heinz M. KabutzDate: 2006-02-08Java Version: 1.1Category: Language
 

Abstract: What do you do when an object cannot be properly constructed? In this newsletter, we look at a few options that are used and discuss what would be best. Based on the experiences of writing the Sun Certified Programmer for Java 5 exam.

 

Welcome to the 120th edition of The Java(tm) Specialists' Newsletter, this time sent from a cold but beautiful village of Hinxton in England. This week I am presenting two Design Patterns Courses at the European Bioinformatics Institute close to Cambridge in the UK. We are having an absolute blast, with stimulating discussions around design and Java.

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Exceptions From Constructors

Last month I wrote the latest Java Programmer Certification examination, version 5.0. An improvement over 1.1, even though some of the questions were still obscure. Hardly any parrot-style memorization was needed, but you did need an excellent understanding of Java 5. Some tips for the Sun Certified Programmer for the Java 2 Platform, Standard Edition 5.0:

  1. Read every question carefully. Then read it again.
  2. Read the source code carefully. They are not trying to trick you. Sun has improved the exams to test your understanding, not memory.
  3. Try to think of what the question is trying to test.
  4. If you find questions which have no correct answers (I found at least one), bang your head against the desk.

I may not reveal any questions, or give hints of what may come up. You can get that information from Sun's website.

Now onto the topic of the newsletter, throwing exceptions from constructors.

What is the best way to deal with objects that cannot be properly instantiated? Here are some suggestions, which I will explore in more detail:

  1. Ignore the problem and cause errors when you try to use the object later on
  2. Throw a checked exception
  3. Throw an IllegalArgumentException
  4. Throw a NullPointerException
  5. Throw an AssertionError
  6. Put in a Java 1.4 assertion

Let's deal with each suggestion.

Ignore the Problem

This, believe it or not, is the most common approach in practice.

Input parameters are not adequately checked to ensure that they are within specification. As a result, the code fails later, rather than immediately. You want to know about errors as soon as possible. The sooner you see the problem, the easier it is to understand what caused it.

With this approach, completely innocent code experiences spurious values or runtime exceptions.

So, whilst this is the most undesirable of all approaches, it is the most common. I suggest you look in your code to see where it is possible to construct objects that actually should never see the light of day.

Throw a Checked Exception

This tells the client that is constructing the object that something bad may happen when you try to make it. Examples are java.net.Socket and java.io.FileInputStream. In the first case, you might try to open a socket to an invalid address, in the second, the file might not be found.

Whilst this approach is not bad, it should only be used for situations that are beyond the control of the client using your objects.

It is debatable whether FileNotFoundException should be thrown by the constructor of FileInputStream. Ideally the client should first verify that the file exists before making an instance of it. However, since the operating system is beyond the control of Java, you cannot guarantee atomicity.

Throw an IllegalArgumentException

This is in my opinion usually the best approach. It expresses to the user accurately what the problem is - he presented an incorrect argument to the constructor.

To be fair to the user, document in your JavaDoc that you will throw the IllegalArgumentException, together with the conditions under which it will be thrown.

public class Person {
  private final String name;
  private final int age;
  private static final int MAXIMUM_AGE = 150;

  /**
   * Person constructor representing a natural
   * person.  Name may not be null.  Age must be
   * non-negative and less than MAXIMUM_AGE.
   *
   * @throws IllegalArgumentException if name is
   * null or if age is out of range.
   */
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
    if (this.age < 0 || this.age > MAXIMUM_AGE) {
      throw new IllegalArgumentException(
          "age out of range: " + this.age +
              " expected range 0 <= age < " +
              MAXIMUM_AGE);
    }
    if (this.name == null) {
      throw new IllegalArgumentException(
          "name is null");
    }
  }
}

Throw a NullPointerException

NullPointerException was the first bug that I had to fix in some "legacy" code, back in 1997. I usually equate "NullPointerException" with bug. Not a bug in the client code, but rather in the library that I am using. Examples of code that throws NullPointerException deliberately includes the Hashtable's put() method. The old Hashtable was written to not handle null values. This was corrected in the java.util.HashMap.

import java.util.*;

public class HashTableTest {
  public static void main(String[] args) {
    System.out.println("HashMap test");
    HashMap hm = new HashMap();
    hm.put(null, "hello");
    System.out.println("Hashtable test");
    Hashtable hs = new Hashtable();
    hs.put(null, "hello");
  }
}

If you decide to go this route, make sure to document this fact to the user. Yes, no one reads comments, but at least you will be covered. You can point to the javadoc and say: "Didn't you read my comment?"

Throw an AssertionError

This is probably the worst way to signal to your user that you could not construct the object due to the parameters he sent you. An AssertionError should only be thrown in places where it cannot happen. If you see this Error, you know that something completely strange has occurred. For example, you have managed to divide an int by zero.

Put in a Java 1.4 Assertion

Another bad idea is putting Java 1.4 assertions into your code. You can switch them on and off at runtime, but the problem I have with that approach is that you may end up with a bad object, or you may not.

To summarise, in my opinion, the most correct approach is to be strict in your constructors and throw IllegalArgumentException or IllegalStateException if you encounter something you do not like.

Right, off to bed to catch up on some beauty sleep. Tomorrow we continue with the Adapter pattern and will discuss whether the MouseAdapter is an Object Adapter or a Class Adapter. It was quite fun today showing my class how to build dynamic proxies.

Kind regards

Heinz

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...