13

A lot of my functions have a whole load of validation code just below the declarations:

if ( ! (start < end) ) {
    throw new IllegalStateException( "Start must be before end." );
    }

I'd like to precisly specify the valid ranges of certain inputs - for example a A > B, C => 1 or str_d.length() > 0.

Given that some of my functions have quite a lot of arguments which must be validated I can end up writing a lot of boiler-plate just to validate the pre-conditions. I'm writing a library which is mainly going to be used by non-technical developers, we've found that validating function inputs is the best way to help our users operate our API correctly. The sooner we raise an error the less work our customers will have to do.

Is there a more elegant method to specify the pre-conditions, post-condtions (and possibly the invariant conditions) in my methods.

A colleague told me about a feature of the Eiffel programming language which allows pre/post/invariant conditions to be described in very natural ways without repeating a lot of boilerplate code. Is there an add-on to the Java language which will allow me to use some of this magic?

Salim Fadhley
  • 22,020
  • 23
  • 75
  • 102

8 Answers8

13

Guava's Preconditions class is just for this. You typically use it with static imports, so your example would look like:

checkArgument(start < end, "Start must be before end");

It makes it easy to add more information to the message as well, without paying the cost of String concatenation if the check passes.

checkArgument(start < end, "Start (%s) must be before end (%s)", start, end);

Unlike assert statements, these can't be disabled.

ColinD
  • 108,630
  • 30
  • 201
  • 202
6

How about assert start < end. Have a look at the documentation.

nfechner
  • 17,295
  • 7
  • 45
  • 64
  • 1
    Yes, I'm primarily a pthon developer. This is the syntax we would use in Python, however I note that this kind of assertion is typically disabled at runtime. Were end < start the answer would be nonsense and the user might waste hours debugging the problem. I need to immediatly raise an error. – Salim Fadhley Jul 25 '11 at 13:01
  • `assert start < end` throws `AssertionError` [documentation](http://docs.oracle.com/javase/7/docs/technotes/guides/language/assert.html) - it is not correct work in case if we want check only method's arguments. `checkArgument()` throws `IllegalArgumentException` and it is correct at business look – Serhii Povísenko Nov 09 '15 at 21:18
  • assertions are disabled by default (hence won't run in production); pass `-ea` to JVM Args to enable them during development – KrishPrabakar Jan 09 '20 at 08:46
  • Looking at my answer almost nine years later, I would definitely suggest to go with the answer by @ColinD in https://stackoverflow.com/a/6816469/218454 and use Guava. – nfechner Mar 04 '20 at 07:51
6

Check out the Cofoja project which provides contracts for Java through annotations. It provides Pre-/Postconditions and Invariants. Also in contrast to other Java implementations it correctly handles contracts defined in parent classes/interfaces. Contract evaluation can be enabled/disabled at runtime.

Here is a code snippet from their tutorial:

import com.google.java.contract.Invariant;
import com.google.java.contract.Requires;

@Invariant("size() >= 0")
interface Stack<T> {
  public int size();

  @Requires("size() >= 1")
  public T pop();

  public void push(T obj);
}
Arno Fiva
  • 1,459
  • 1
  • 13
  • 17
4

Aspect oriented programming can be used for such a problem. Method calls can be intercepted checking for the invariant. Pointcuts and advices are configured in a declarative way. Spring and Guice make usage of AOP straightforward.

Here's an example in Guice.

Boris Pavlović
  • 63,078
  • 28
  • 122
  • 148
  • 1
    This is interesting - can you point me to an example in Guice? – Salim Fadhley Jul 25 '11 at 12:59
  • 1
    I don't think that Guice/AOP is a good suggestion for general precondition checking. It's great for large cross-cutting checks (particularly ones you might want to just ingore in tests) like "is the person who made the request a logged in user", but more specific checks like "is A < B?" would be terribly unwieldy at best with it. – ColinD Jul 25 '11 at 14:27
4

You might be able to do this with annotation and aspect orientated programming.

I would use IllegalArgumentException if an argument combination is not legal. I would use IllegalStateException is in a state which prevents the method from working.

You can create a helper method for the exception.

public static void check(boolean test, String message) {
    if(!test) throw new IllegalArgumentException(message);
}

check(start < end, "Start must be before end.");
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

For input validation, you can also use Apache Commons Validator.

Note that input validation should always be enabled. Hence it is conceptually very different from assertion checking (as, e.g., in Eiffel), which can be optionally on/off -- see the answer to this related stack overflow question When should I use Apache Commons' Validate.isTrue, and when should I just use the 'assert' keyword?

Community
  • 1
  • 1
avandeursen
  • 8,458
  • 3
  • 41
  • 51
1

If I find myself repeating the same boiler-plate precondition checking code within a class, I refactor my code to reduce the duplication and to increase abstraction by extractung the repeated code into a new (static private) method. I use the Java-7 Objects.requireNonNull method for null checks.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • This has to be the best option if you are building out an SDK that others will use. No additional dependencies needed. :) – Carnell May 12 '15 at 04:13
-2

The JUnit package has constructs like assert that would aid in doing such condition check.

Milhous
  • 14,473
  • 16
  • 63
  • 82
  • you don't merely have to use it in unit tests, you should be able to use it at runtime also. – Milhous Jul 25 '11 at 13:10
  • 1
    because test libraries are considered to be used in test code only and must not be a transitive dependency of your productive code. otherwise your dependending libraries/projects will suffer from that. – vvursT Jun 10 '14 at 10:48