Take the 2-minute tour ×

Let's assume we have a class Student with the following constructor:

/** Initializes a student instance.
 * @param matrNr    matriculation number (allowed range: 10000 to 99999)
 * @param firstName first name (at least 3 characters, no whitespace) 
public Student(int matrNr, String firstName) {
    if (matrNr < 10000 || matrNr > 99999 || !firstName.matches("[^\\s]{3,}"))
        throw new IllegalArgumentException("Pre-conditions not fulfilled");
    // we're safe at this point.

Correct me if I'm wrong, but I think in this example, I followed the design by contract paradigm by simply specifiying the (rather static) constraints on the possible input values and raising a generic, unchecked exception if those are not fulfilled.

Now, there is a backend class that manages a list of students, indexed by their matriculation number. It holds a Map<Integer, Student> to save this mapping and provides access to it through an addStudent method:

public void addStudent(Student student) {
    students.put(student.getMatrNr(), student);

Now let's assume there is a constraint on this method like "a student with the same matriculation number must not already exist in the database".

I see two options of how this could be realized:

Option A

Define a custom UniquenessException class that is raise by addStudent if a student with the same matr. number already exists. Calling code will then look something like this:

try {
    campus.addStudent(new Student(...));
catch (UniquenessError) {
    printError("student already existing.");

Option B

State the requirement as a pre-condition and simply raise an IAE if it doesn't hold. Additionally, provide a method canAddStudent(Student stud) that checks in advance whether addStudent will fail. Calling code would then look something like this:

Student stud = new Student(...);
if (campus.canAddStudent(stud))
    printError("student already existing.");

I feel that option A is much cleaner from a software-engineering point of view, for at least the following reason:

  • It can easily be made thread-safe without modifying the calling code (Thanks to Voo for pointing me to TOCTTOU, which seems to describe that exact issue)

Thus I wonder:

  1. Is there a third option which is even better?
  2. Does option B have an advantage that I didn't think of?
  3. Would it actually be allowed from a design by contract point of view to use option B and define the uniqueness as a pre-condition of the addStudent method?
  4. Is there a rule of thumb when to define pre-conditions and simply raise IAE and when to use "proper" exceptions? I think "make it a pre-condition unless it depends on the current state of the system" could be such a rule. Is there a better?

UPDATE: It seems like there is another good option, which is to provide a public boolean tryAddStudent(...) method that doesn't throw an exception but instead signals error/failure using the return value.

share|improve this question
99999 as an upper bound for matriculation number seems low. Would take less than 12 years at my university to get out of bounds. –  Fabian Barney Feb 13 '12 at 17:20
@Fabian: I am not actually trying to write some student management software. This is barely an example (didn't I meantion it?) –  Niklas B. Feb 13 '12 at 17:21

2 Answers 2

up vote 2 down vote accepted

(this is too long for a comment)

In your option B, I wouldn't use a Map<Integer,Student> and then do:

if (campus.hasStudent(12000)) 
    printError("student already existing.");
    campus.addStudent(new Student(...));

The Map abstraction isn't practical enough for your use case (you're mentionning concurrency issues), I'd use instead a ConcurrentMap<Integer,Student> and do something like this:

final Student candidate = new Student(...);
final Student res = putIfAbsent(student.getMatrNr(), candidate)
if ( res != null ) {
    throw new IllegalStateException("Class contract violation: \"student already exists!\", please read the doc");
share|improve this answer
So we could just as well use a boolean return value to signal success/failure? –  Niklas B. Feb 13 '12 at 16:57
Actually it should be if (res == null) I think. –  Voo Feb 13 '12 at 17:03
@Voo: oops... Fixed the code. That said in this case it's if ( res != null ) because we want to throw the exception if there was already a value. –  TacticalCoder Feb 13 '12 at 17:09
@NiklasB.: "So we could just as well use a boolean return value to signal success/failure?" [sic] I'm not saying you should (or should not) use exception in your case. All I'm saying is using the regular Map abstraction isn't very powerful because you'd have to deal yourself with synchronization issues. So use a ConcurrentMap instead and leverage its putIfAbsent method (which already takes care of nasty synchronization issues for you). Now it's up to you to see if you want to use a return value (as putIfAbsent does), a boolean or throw an exception (I'm just commenting on the Map). –  TacticalCoder Feb 13 '12 at 17:12
@user988052: Yeah, the question actually was targetted at whether or not it's sensible to use exceptions vs. pre-conditions here and not so much at how to make this thread-safe (after all, it's quite easy even when using manual synchronization). But thanks for the hint, the test and set approach is also applicable to the actual issue (provide something like a tryAddStudent method). –  Niklas B. Feb 13 '12 at 17:14

I don't believe the way that the backend class manages a list of students would be relevant to the contract--that is, that it holds a Map<Integer, Student> would not be part of the contract. Thus bringing the matriculation number into the contract in hasStudent(int matrNr) seems a little evil too.

I'd suggest the the campus probably should have a method Boolean hasStudent(Student student), which would check to see if the campus has the student based on whatever the condition. If uniqueness is required by contract, and is truly exceptional, you would then use the contractual check:

   Student student= new Student(int matrNbr, String name);
   if (campus.hasStudent(student) {
      throw new UniquenessException();
   else {

The exceptions thrown should be as relevant to the contract as the arguments and return values


If the add should simply fail if uniqueness is not met and is not exceptional, then don't throw the exception. Instead, make the success of the add a return value (as in java.util.HashSet.add()). This way, campus.add(Student) would return true if the student was actually added.

share|improve this answer
What's the advantage of a boolean return value instead of an exception? –  Niklas B. Feb 13 '12 at 17:07
Exceptions indicate exceptional cases, and must be handled. Checking the boolean is optional--the calling class may or may not care if the new instance of the student was actually added or not. Also, exceptions are expensive, as they have to bundle in the stack trace and such. –  Matthew Flynn Feb 13 '12 at 17:12
Thanks for the clarificiation :) –  Niklas B. Feb 13 '12 at 17:13

Your Answer


