Contracts are a common concept in the design and analysis of algorithm correction. The concept is that the functions, methods or other units of our programs must have:
-
Preconditions: Conditions that callers must provide in advance for the method to produce correct results;
-
Postconditions: Conditions that (given preconditions) the method promises will be given when you return;
-
Invariant: Conditions that the method promises will not affect-will be true or false on return if and only if they were on entry.
In this framework, the try { ... } finally { ... }
is a useful construct so that our code can guarantee postconditions even when exceptions are given within the block of try { ... }
or in any method invoked within this block. Since the commands within the finally { ... }
block are always going to be executed, exception or not, it is common to put there code that guarantees the postconditions.
The examples of "cleaning" that others have given fall within this framework. The idea is that in these examples:
A resource such as a file or a connection to a database is used;
There is a postcondition that forces the example code to guarantee that the resource will be closed no matter what happens in its use.
This was the most common use of finally
in old Java versions. In Java 7 a new construct was introduced, called "try with resources," which replaces the try { ... } finally { ... }
in its most common uses:
try (OutputStream out = ...) {
// usar el OutputStream 'out'
} // postcondición: 'out' ha sido cerrado
Another kind of example is with the thread synchronization tools ("threads"). In the package java.util.concurrent.locks
there are several classes of locks ("locks") that are normally used with try {...} finally {...}
:
Lock l = ...;
l.lock();
try {
// Entre los hilos que comparten la cerradura 'l', sólo uno
// puede entrar en este bloque a la vez.
} finally {
l.unlock();
} // postcondición: la cerradura está abierta
But the concept of postconditions should be emphasized again. After all, the try { ... } finally { ... }
is not specifically a "cleaning" mechanism but rather a general tool to facilitate the composition of correct programs. So it has other uses, even if they are much less common than these two.
It is possible to add another type of examples. A frequent cause of programming errors is the early return ("early return") of a function or method. The typical cases are something like this:
public LoQueSea miMétodo(Desto algo, Coso queSéYo) {
// muchas lineas...
if (noSéQué()) {
return new LoQueSea(algo); // salida temprana
}
// muchas más lineas...
cosaQueSiempreTieneQuePasar(queSéYo);
return new LoQueSea(algo.con(queSéYo));
}
Assuming that it is always necessary to call the cosaQueSiempreTieneQuePasar()
method, this example has an error: when noSéQué()
returns with true
we leave early and forget to call cosaQueSiempreTieneQuePasar()
. A solution in many cases is the following:
public LoQueSea miMétodo(Desto algo, Coso queSéYo) {
try {
// muchas lineas...
if (noSéQué()) {
return new LoQueSea(algo); // salida temprana
}
// muchas más lineas...
return new LoQueSea(algo.con(queSéYo));
} finally {
// Este bloque se ejecuta siempre, aun si hay
// salida temprana.
cosaQueSiempreTieneQuePasar(queSéYo);
}
}
And here it should be noted that, although many answers have mentioned exceptions as the reason for the try {...} finally {...}
, this example shows that we can run into problems even if there are no exceptions. Other examples are possible with break
and continue
in repeating structures as for (...) {...}
.
Or rather, the exceptions, the return
early, the break
and the continue
have something in common-early exit from a block of code-and the finally
is a common solution for problems that cause all of these.