How do I insert a precondition in a java class method or constructor?
Asked Answered
H

5

7

This is for a java class I'm taking. The book mentions preconditions and postconditions but doesn't give any examples how to code them. It goes on to talk about asserts, I have that down, but the assignment I'm doing specifically states to insert preconditions and test the preconditions with asserts.

Any help would be great.

Henandchickens answered 18/11, 2012 at 18:53 Comment(4)
I think you might wrap your constructor or a method into another cnostructor or a method and implement pre- or postconditions thereCambridgeshire
public MyClass() { assert <precondition>; ... stuff ... assert <postcondition>; } -- there is really nothing special about it. Note that no real code would put those postconditions in. That stuff is tested from the outside, by unit tests.Paralytic
How about throwing exceptions like Eifel shows in their toturials?Korey
A precondition is not really something you code. It is part of the contract for a method. You can write code that behaves in some way if a precondition fails. In Java the convention is to throw a RuntimeException. But that is not really coding the precondition.Inverson
T
5

Languages like Eiffel support "preconditions" and "postconditions" as a basic part of the language.

One can make a compelling argument that the whole purpose of an "object constructor" is precisely to establish "the class invariant".

But with Java (as with just about every other post-C++ object oriented language), you pretty much have to fake it.

Here's an excellent tech note on leveraging Java "assert":

Teeters answered 18/11, 2012 at 18:57 Comment(2)
Thank you for the link, I found info there that helped me a bunch!Henandchickens
The link is now dead. :(Conn
C
5

Some examples given here express preconditions as parameter validation, and exercise them in assertions. A non-private method should always perform parameter validation as its caller is out of scope of its implementation.

I tend to argue that parameter validation does not constitute a precondition, in the context of object oriented systems - although I see plenty of examples of this approach in the google-sphere.

My understanding of contracts started with [BINDER1999]; which defined invariant, precondition, and postconditions in terms of the state of the object-under-test; expressed as Boolean predicates. This treatment considers how the state-space encapsulated by a class is managed, in which methods represent transitions between states.

Discussion of pre- and post-conditions in terms of parameter and return values is much easier to convey than discussions in terms of state-spaces! So I can see why this view is much more prevalent.

To recap, there are three types of contract under discussion:

  • The invariant is a test on the object-under-test which is true from the end of construction (any constructor), to the start of its destruction (although it may be broken while a transition is taking place).
  • A pre-condition is a test on the object-under-test which must be true for the method-under-test to be invoked on the object-under-test.
  • A post-condition is a test on the object-under-test which must be true immediately after the method-under-test completes.

If you adopt the (sensible) approach that overloaded methods must be semantically equivalent, then pre- and post-conditions are the same for any overload of a given method in a class.

When interitance and overridden methods are considered, contract-driven-design must follow the Liskov Substitution Principle; which results in the following rules:

  • The invariant of a derived class is the logical-AND of its local invariant, and that of its base class.
  • The pre-condition of an overridden method is the logical-OR of its local pre-condition, and that of the method in its base class.
  • The post-condition of an overridden method is the logical-AND of its local post-condition, and that of the method in its base class.

Remember, of course, that whenever a pre- or post-condition is tested, the invariant for the class-under-test must also be tested.

In Java, contracts can be written as protected Boolean predicates. For example:

    // class invariant predicate
    protected bool _invariant_ ()
    {
        bool result = super._invariant_ (); // if derived
        bool local  = ... // invariant condition within this implementation
        return result & local;
    }
    
    protected bool foo_pre_ ()
    {
        bool result = super.foo_pre_ (); // if foo() overridden
        bool local  = ... // pre-condition for foo() within this implementation
        return result || local;
    }
    
    protected bool foo_post_ ()
    {
        bool result = super.foo_post_ (); // if foo() is overridden
        bool local  = ... // post-condition for foo() with this implementation
        return result && local;
    }
    
    public Result foo (Parameters... p)
    {
        boolean success = false;
        assert (_invariant_ () && foo_pre_ ()); // pre-condition check under assertion
        try
        {
            Result result = foo_impl_ (p); // optionally wrap a private implementation function
            success = true;
            return result;
        }
        finally
        {
            // post-condition check on success, or pre-condition on exception
            assert (_invariant_ () && (success ? foo_post_ () : foo_pre_ ());
        }
    }
    
    private Result foo_impl_ (Parameters... p)
    {
        ... // parameter validation as part of normal code
    }

Don't roll the invariant predicate into the pre- or post-condition predicates, as this would result in the invariant being called multiple times at each test-point in a derived class.

This approach uses a wrapper for the method-under-test, the implementation for which is now in a private implementation method, and leaves the body of the implementation unaffected by the contract assertions. The wrapper also handles exceptional behaviour - in this case, if the implementation throws and exception, the pre-condition is checked again, as expected for an exception-safe implementation.

Note that if, in the example above, 'foo_impl_()' throws an exception, and the subsequent pre-condition assertion in the 'finally' block also fails, then the original exception from 'foo_impl_()' will be lost in favour of the assertion failure.

Please note that the above is written off the top-of-my-head, so may contain errors.

Reference:

  • [BINDER1999] Binder, "Testing Object-Oriented Systems", Addison-Wesley 1999.

Update 2014-05-19

I have gone back-to-basics with regards to contracts on input and outputs.

The discussion above, and based on [BINDER1999], considered contracts in terms of the state-space of objects-under-test. Modelling classes as strongly encapsulated state-spaces is fundamental to building software in a scalable manner - but that is another topic...

Considering how the Liskov Substitution Principle (LSP) 1 is applied (and required) when considering inheritance:

An overridden method in a derived class must be substitutable for the same method in the base class.

To be substitutable, the method in the derived class must not be more restrictive on its input parameters than the method in the base class - otherwise then it would fail where the base class method succeeded, breaking LSP 1.

Similarly the output value(s) and return type (where not part of the method signature) must be substitutable for that produced by the method in the base class - it must be at least as restrictive in its output values otherwise this also would break LSP 1. Note that this also applies to the return type - from which rules on co-variant return types can be derived.

Therefore contracts on the input and output values of an overridden method follow the same rules for combination under inheritance and pre- and post-conditions respectively; and in order to implement these rules effectively they must be implemented separate from the method to which they apply:

    protected bool foo_input_ (Parameters... p)
    {
        bool result = super.foo_input_ (p); // if foo() overridden
        bool local  = ... // input-condition for foo() within this implementation
        return result || local;
    }
    
    protected bool foo_output_ (Return r, Parameters... p)
    {
        bool result = super.foo_output_ (r, p); // if foo() is overridden
        bool local  = ... // output-condition for foo() with this implementation
        return result && local;
    }

Note that these are almost identical to foo_pre_() and foo_post_() respectively, and should be called in the test harness at the same test-points as these contracts.

The pre- and post-conditions are defined for a method-family - the same conditions apply to all overloaded variants of a method. The input and output contracts apply to a specific method signature; however, to use these safely and predictably, we must understand the signature-lookup rules for our language and implementation (cf. C++ using).

Note that in the above, I use the expression Parameters... p as short-hand for any set of parameter types and names; it is not meant to imply a variadic method!

Course answered 2/5, 2014 at 14:11 Comment(0)
B
0

Just use assert to code the preconditions. For example:

...
private double n = 0;
private double sum = 0;
...
public double mean(){
  assert n > 0;
  return sum/n;
}
...
Bastian answered 18/11, 2012 at 19:3 Comment(0)
S
0

A precondition is a condition that has to hold at given point in the execution of a program, if the execution of the program is to continue correctly. For example, the statement "x = A[i];" has two preconditions: that A is not null and that 0 <= i < A.length. If either of these preconditions is violated, then the execution of the statement will generate an error.

Also, a precondition of a subroutine is a condition that has to be true when the subroutine is called in order for the subroutine to work correctly.

Here is Detail &

Here is example: preconditions-postconditions-invariants

Shrieval answered 18/11, 2012 at 19:4 Comment(0)
B
0

To apply preconditions and postconditions technique in Java you need to define and execute assertions at runtime. It actually allows to define runtime checks inside your code.

public boolean isInEditMode() {
 ...
}

/**
* Sets a new text.
* Precondition: isEditMode()
* Precondition: text != null
* Postcondition: getText() == text
* @param name
*/
public void setText(String text) {
 assert isInEditMode() : "Precondition: isInEditMode()";
 assert text != null : "Precondition: text != null";

 this.text = text;

 assert getText() == text : "Postcondition: getText() == text";
}

/**
* Delivers the text.
* Precondition: isEditMode()
* Postcondition: result != null
* @return
*/
public String getText() {

 assert isInEditMode() : "Precondition: isInEditMode()";

 String result = text;

 assert result != null : "Postcondition: result != null";
 return result;
}

Please follow details of the above code here

Borscht answered 18/11, 2012 at 19:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.