Under the hood

C4J use the -javaagent switch that was added to Java 1.5. The javaagent concept provides a way to hook on to the class loader, and return custom byte code for any class that is loaded by the Java runtime. C4J contains an instrumentor class that instruments all loaded classes that are protected by a contract class.

Javassist, an extremely easy to use byte code library, is used for instrumenting.

The C4J instrumentor class does the following on each class that is loaded:
  • Does the target class or any of it's parents contain a valid ContractReference annotation? Or is an external contract attached to the target class or any of it's parents? If not, don't instrument this class.
  • If the target class has a direct contract (as opposed to inherited from a parent), insert a private member of the contract class in to the target class.
    For each method in the target class:
  • If a precondition exists in the contract or the parents contract, add a call to it at the beginning of the method.
  • If a postcondition exists in the contract or the parents contract, add a call to it at the end of the method.
  • If a classInvariant exists in the contract or the parents contract, add a call to it at the end of the method.
    For all of the above:
  • If both a direct contract and an inherited contract exist for the method, add method calls to both, according to the rules of contract inheritance.

Apart from this C4J provides a number of features to facilitate the writing of contract classes.
These features can be accessed by using C4J's annotations and static methods.

Please note that the syntax of first generation C4J contracts is not compatible to the syntax of second gneration C4J contracts as the C4J syntax had to be changed in order to fulfill agility requirements. The second generation C4J version number has a new meaning: The version number now corresponds to the Java version number starting from which C4J is upward compatible, i.e. C4J 6.x is compatible to Java 6, Java 7 and so on.

Please be aware of the fact that for contract inheritance C4J deals in a way with the Liskov substitution principle (LSP) that is different to Eiffel.

In Eiffel contract inheritance is implemented like this:
  • superclass preconditions and subclass preconditions of the same method are concatenated using OR.
  • superclass postconditions and subclass postconditions of the same method are concatenated using AND.
  • superclass class invariants and subclass class invariants are concatenated using AND.
  • Opposed to that, first generation C4J implements the inheritance of contracts like this:
  • superclass preconditions and valid subclass preconditions of the same method are concatenated using AND.
  • superclass postconditions and valid subclass postconditions of the same method are concatenated using AND.
  • superclass class invariants and subclass class invariants are concatenated using AND.
  • According to LSP valid subclass preconditions are preconditions which do not strengthen the superclass preconditions, i.e. the subclass precondition always has to prove true when the superclass precondition proves true. This is checked by C4J at runtime using the AND concatenation. If a subclass precondition is detected to break that rule at runtime, a C4J ERROR message is logged and the affected subclass precondition is discarded.

    According to LSP valid subclass postconditions are postconditions which do not weaken the superclass postconditions, i.e. the subclass postcondition always has to prove false when the superclass postcondition proves false. This is checked by C4J at runtime using the AND concatenation. If a subclass postcondition is detected to break that rule at runtime, a C4J ERROR message is logged and the affected subclass postcondition is discarded.

    Please note that Eiffel does not detect invalid preconditions as they are concatenated using OR. That is the reason why Richard Mitchell and Jim McKim give the following guideline in their book Design by Contract by Example:

    Guideline 4: To support redefinition of features, guard each postcondition clause with its corresponding precondition.


    A questionable guideline as it creates a lot of redundancy and makes contracts complex.

    C4J contracts do not need such a questionable guideline as the AND concatenation of preconditions guarantees the ability of redefining (overriding) features (methods) in subclasses.

    Second generation C4J is even more rigid and implements the inheritance of contracts like this:
    • superclass preconditions of a method are not allowed to be extended by any subclass.
    • superclass postconditions and subclass postconditions of the same method are concatenated using AND.
    • superclass class invariants and subclass class invariants are concatenated using AND.
    The rationale of that even more rigid implementation for precondition inheritance is:
    • A precondition can only prove false or true.
    • Case 1: The superclass precondition proves false. An AssertionError has to be thrown as a precondition violation is detected regardless whether the subclass precondition may prove true or false. The AND concatenation of superclass and subclass preconditions is perfectly right to implement that expected behaviour.
    • Case 2: The superclass precondition proves true. For this case it matters, whether the subclass precondition proves true or false.
    • Case 2.1: The subclass precondition proves false. That is a LSP violation as the subclass precondition strengthens its superclass precondition.
    • Case 2.1: The subclass precondition proves true. That is in accordance with the LSP as the subclass precondition does not strengthen its superclass precondition. There are two subcases for this case.
    • Case 2.1.1: The subclass precondition equals its superclass precondition, i.e. the subclass precondition always has to prove true when the superclass precondition proves true.
    • Case 2.1.2: The subclass precondition weakens its superclass precondition, the subclass precondition always has to prove true when the superclass precondition proves true.
    • As there is no difference in the effective result of case 2.1.1 and case 2.1.2 you can merge them to one constraint: As LSP says that preconditions may not be strengthend C4J demands that superclass preconditions are not allowed to be extended which has the same effective result.
    As C4J also supports multiple inheritance of interfaces. The implementation is the same for first and second generation C4J and looks like that:
    • all superinterface preconditions of the same method are merged using AND.
    • all superinterface postconditions of the same method are merged using AND.
    • all superinterface class invariants are merged using AND.
    The C4J implementation of contract inheritance is in accordance to the LSP paper of Barbara Liskov Behavioral Subtyping Using Invariants and Constraints (1999).

    Fore more information about the discussion of that topic please have a look at Jonas Bergströms answer in the first generation C4J forum (November 2006).