4

I am learning about preconditions and when to use them. I have been told that the precondition

@pre fileName must be the name of a valid file

does not suit in the following code:

/**
Creates a new FileReader, given the name of file to read from.
@param fileName- the name of file to read from
@throw FileNotFoundException - if the named file does not exist,
is a directory rather than a regular file, or for some other reason cannot
be opened for reading.
*/
public FileReader readFile(String fileName) throws FileNotFoundException {
. . .
}//readFile

Why is this?

Edit: Another example

We are assuming that the following, as an example, is done the "right" way. Note the IllegalArgumentException and the precondition. Note how the behavior is well defined, and how the throws declaration is made even though a precondition is set. Most importantly, notice how it doesn't contain a precondition for the NullPointerException. Once again, why doesn't it?

/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start
* @pre start <= end
* @post The time span of the returned period is positive.
* @throws IllegalArgumentException if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end) f

Are these examples avoiding use of extra preconditions? One could argue that if we are avoiding preconditions, then why have them at all? That is, why not replace all preconditions with @throws declarations (if avoiding them is what is done here)?

Datoraki
  • 1,223
  • 13
  • 26
  • @pre anonotation is not part of standard Java, AFAIK , nor formal "preconditions". perhaps you are using some library? – leonbloy Apr 19 '11 at 19:25
  • True, however, these kind of annotations are not too seldom seen when Designing by Contract in Java. A random example on the web: http://www.mmsindia.com/DBCForJava.html#Precondition – Datoraki Apr 19 '11 at 21:32
  • Some more random examples from google: http://modernjass.sourceforge.net/ http://portal.acm.org/citation.cfm?id=832856 And this might be a better explanation of what it all means: http://www.cs.olemiss.edu/~hcc/distObj/notes/OW_Berlin99_web.pdf – Datoraki Apr 19 '11 at 21:46

3 Answers3

5

Wikipedia defines precondition as:

In computer programming, a precondition is a condition or predicate that must always be true just prior to the execution of some section of code or before an operation in a formal specification.

If a precondition is violated, the effect of the section of code becomes undefined and thus may or may not carry out its intended work.

In your example, the effect of the method if the filename is invalid is defined (it must throw a FileNotFoundException).

Put differently, if file being valid were a precondition we'd know that it was always valid, and the part of the contract that mandates an exception is thrown it is wasn't would never apply. Unreachable specification cases are a code smell just like unreachable code.

Edit:

If I have some preconditions, and can provide defined behavior for these conditions, wouldn't it be better if I did so?

Of course, but then it's no longer a precondition as defined by Hoare. Formally speaking, that a method has precondition pre and postcondition post means that for each execution of the method that started in state prestate and ended in state poststate

pre(prestate) ==> post(poststate)

If the left hand side of the implication is false, this is trivially true irrespective of what poststate is, i.e. the method will satisfy its contract irrespective of what it does, i.e. the method's behaviour is undefined.

Now, fast forward to modern times, where methods can throw exceptions. The usual way to model exceptions is to treat them as special return values, i.e. whether an exception occurred is part of the postcondition.

The exception is not really unreachable though, is it?

If the throws clause is part of the postcondtion, you have something like:

pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)

which is logically equivalent to

pre(prestate) ==> (pre(prestate) and return_valid)

that is, it doesn't matter whether you write that throws clause, which is why I called that specification case unreachable.

I would say that an exception rather works as a supplement to the precondition to inform the user of what's going to happen if he/she breaks the contract.

No; the throws clause is part of the contract, and as such carries no weight if the contract is broken.

Of course it is possible to define that @throws clauses need to be satisfied irrespective of the precondition, but is that useful? Consider:

@pre foo != null
@throws IllegalStateException if foo.active

Must the exception be thrown if foo is null? In the classic definition, it is undefined, because we assume nobody will pass null for foo. In your definition, we have to explicitly repeat that in every throws clause:

@pre foo != null
@throws NullPointerException if foo == null
@throws IllegalStateException if foo != null && foo.active

If I know no reasonable programmer is going to pass null to that method, why should I be forced to specify that case in my specification? What benefit does it have to describe behavior that is not useful to the caller? (If the caller wants to know whether foo is null, he can check it himself rather than calling our method and catching the NullPointerException!).

meriton
  • 68,356
  • 14
  • 108
  • 175
  • Thanks for your answer, but I just can't agree with you here. It sounds like wikipedia suggests that the behavior is undefined in general. I think you are making it sound a bit like undefined behavior is desirable. If I have some preconditions, and can provide defined behavior for these conditions, wouldn't it be better if I did so? What I am saying is that it doesn't make much sense for undefined behaviour to be a requirement for calling a precondition a precondition. – Datoraki Apr 19 '11 at 21:20
  • As for unreachable specification cases: The exception is not really unreachable though, is it? I would say that an exception rather works as a supplement to the precondition to inform the user of what's going to happen if he/she breaks the contract. Wouldn't you agree? – Datoraki Apr 19 '11 at 21:22
  • "Of course, but then it's no longer a precondition as defined by Hoare." Ok so: 1. It is better to define behavior when possible. 2. A precondition results in undefined behavior. => It is better not to use preconditions and only use @throws declarations instead. Then, why do preconditions exist? Do you think the second example should remove the @pre? – Datoraki Apr 20 '11 at 07:39
  • "we have to explicitly repeat that in every throws clause" I don't understand how adding information about what exception is to be thrown when violating contract, is repeating one self. Even though it is part of the contract, it is not the same part of the contract, as it provides extra information. "and as such carries no weight if the contract is broken." It has the weight that I can read in the doc and see what exception I would get if I did break the contract. Do you think the second example (if using the @pre) should leave out the @throws? – Datoraki Apr 20 '11 at 08:00
  • "pre(prestate) ==> post(poststate) If the left hand side of the implication is false, this is trivially true irrespective of what poststate is." What is then trivially true? If you mean the expression as a whole: The implication is always valid (as long as the variables aren't t -> f ), and should be as well. If you mean the right side: If the left side is false, this says nothing about the right side since this is an implication and not an equivalence. If you mean the left side: If the left side is false, it is definitely not true. It's very unclear what you are saying here. Rephrase? – Datoraki Apr 20 '11 at 09:03
  • "pre(prestate) ==> (pre(prestate) and return_valid) or (not pre(prestate) and throws_ exception)" The thing here though, is that the pre(state) isn't guaranteed to be true, since the client can break the contract. Therefore, just like one adds an @throws on a NullPointerException to inform the client of what's going to happen if the client abuses the method by sending a null pointer, one should add an @throws don't you think? Even though one is not providing extra information as to what input is valid, one is providing extra information on how the method will behave (part of the contract) – Datoraki Apr 20 '11 at 09:26
3

Ok, so this is what I've found out:

Background

Based on the following principles, as described in Bertrand Meyer's book Object Oriented Software Construction:

"Non-Redundancy principle Under no circumstances shall the body of a routine ever test for the routine’s precondition." - Bertrand Meyer

"Precondition Availability rule Every feature appearing in the precondition of a routine must be available to every client to which the routine is available." - Bertrand Meyer

, these two points answer this question:

  1. For preconditions to be useful, the client (user of method) has to be able to test them.
  2. The server should never test the precondition, because this will add complexity to the system. Although, assertions are turned on to do this testing when debugging the system.

More on when, why, and how to use preconditions:

"Central to Design by Contract is the idea, expressed as the Non-Redundancy principle, that for any consistency condition that could jeopardize a routine’s proper functioning you should assign enforcement of this condition to only one of the two partners in the contract. Which one? In each case you have two possibilities: • Either you assign the responsibility to clients, in which case the condition will appear as part of the routine’s precondition. • Or you appoint the supplier, in which case the condition will appear in a conditional instruction of the form if condition then ..., or an equivalent control structure, in the routine’s body.

We can call the first attitude demanding and the second one tolerant." - Bertrand Meyer

So a precondition should only exist if it is decided that the client holds the responsibility. Since the server should not test the precondition, the behaviour becomes undefined (as also stated on Wikipedia).

Answers

  • The first point answers the first example.
  • As for the second example, it is probably not done the right way. This is because the first @throws declaration implies that the method has (other than an assertion) tested the precondition. This violates the second point.

As for the null pointer; this shows that the null pointer responsibility is assigned to the server. That is, using a "tolerant attitude", as opposed to a "demanding attitude". This is perfectly OK. If one chose to implement a demanding attitude, one would remove the throws declaration (but more importantly; not test for it), and add a precondition declaration (and perhaps an assertion).

Community
  • 1
  • 1
Datoraki
  • 1,223
  • 13
  • 26
2

I think that design by contract idea (I don't use it myself yet) and pre/post conditions are intended to guarantee specific conditions incoming and outgoing from the method. Particularly the compiler (in this case, theoretically as Java does not have this built-in) would need to be able to validate the contract conditions. In the case of your file precondition, this cannot be done since the file is an external resource, the class may move and the same file may not be there, etc. How can a compiler (or pre-processor) guarantee such a contract?

On the other hand, if you are just using it for comments then it would at least show the other developer what you expect but you must still expect too that there will be exceptions when the file does not exit.

I think that it "does not suit" the method in the formal sense of design by contract because it cannot be validated for even one case. That is, you may give valid file names in one environment but that may not be valid in other environment external to your program.

On the other hand, the date example, the pre and post conditions can be validated in the caller context as they are not influenced by external environmental setup that the method itself has no control over.

Kevin Brock
  • 8,874
  • 1
  • 33
  • 37
  • Ah =). Of course, the client has to be able to check the preconditions for them to be useful. How about the nullpointer? Why not have a precondition for it? – Datoraki Apr 20 '11 at 08:05
  • @Datoraki you can certainly have a precondition stating a reference must be non-null. This can be enforced. Obviously, if someone still passed in a `null` the runtime engine will still throw a `NullPointerException`. In some compilers/precompilers that support design by contract, this will be fully enforced (e.g. C# 4.0) and then a no null reference is guaranteed. – Kevin Brock Apr 20 '11 at 13:56