How delegates impose contracts on their wrappers

Eric | November 15, 2007

This week I was working a lot on our new PLDI submission, which we just submitted (more about that later). During this work I came around an interesting subtlety with respect to delegates and Design By Contract (DBC) . We wanted to validate the fulfillment of a contract for the Iterator interface: When using an iterator i you are only allowed to call i.next() if before that you have checked whether i.hasNext() holds. For PLDI we developed a static analysis approach (using tracematches) that allows you to flag potential violations of this contract. Here is one that we found in Jython, a Python implementation for the Java Virtual Machine and part of the DaCapo benchmark suite:


public Iterator iterator() {

    return new Iterator() {
        Iterator i = list.iterator();
        public void remove() {
            throw new UnsupportedOperationException();
        }
        public boolean hasNext() {
            return i.hasNext();
	}
        public Object next() {
            return i.next();
	}
    };
}

The code declares an inner class, a wrapper, that delegates its operations to a delegate object i. This happens all the time, for instance whenever using the Decorator Design Pattern.

So what’s so interesting about that code? It turns out that our analysis flagged a potential contract violation in line 9, at the method call i.next(). After all it is true that the anonymous inner class calls next() without checking whether hasNext() holds at that line. However, the inner class is an iterator by itself. Hence, any client of that inner class has to fulfill the Iterator-contract as well! When taking this into account, one can safely assume that the inner class is used correctly and when doing so the call at line 10 is safe.

Therefore, in a way an object that delegates some of its work to a delegate object inherits the contracts of that delegate in return and clients of the wrapper object have to make sure to fulfill those contracts. This is relatively clear in cases like this where the wrapper and the delegate are of the same declared type (Iterator): Of course they both have to fulfill the Iterator contract. If the wrapper is of another type though, I believe things can get more subtle… I wonder if DBC tools like Contract4J deal with this?