DefaultMethods

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(The Sorting Problem)
Line 28: Line 28:
</source>
</source>
-
it works properly on [[JDK]]7, but it gets broken on [[JDK]]8. The code needs to compile on [[JDK]]7, so no [[DefaultMethods]] (introduced in [[JDK]]8) are called. In spite of that, the notification change isn't delivered!
+
it works properly on [[JDK]]7, but it gets broken on [[JDK]]8. The code needs to compile on [[JDK]]7, so no [[DefaultMethods]] (introduced in [[JDK]]8) are called. In spite of that the code behaves differently on [[JDK]]8 and the notification change isn't delivered!
The problem is that the static '''sort''' method in {{JDK|java/util|Collections}} does the sorting by itself in [[JDK]]7, but in [[JDK]]8 it delegates to {{JDK|java/util|List}}'''.sort''':
The problem is that the static '''sort''' method in {{JDK|java/util|Collections}} does the sorting by itself in [[JDK]]7, but in [[JDK]]8 it delegates to {{JDK|java/util|List}}'''.sort''':

Revision as of 10:45, 24 September 2016

DefaultMethods are a new feature of JDK8 which breaks the clear separation between Java interface (only specifies a contract) and class (provides some implementation). Many members of the Java community were crying for having a way to add methods into already published interface in a backward compatible way for ages. Of course, as usual in Java, only when the JDK team felt the need itself (because of adding a lot of new Lambda methods into Collection & co. classes), it listen to the general request.

On the other hand, there were people claiming that DefaultMethods are bad - that an interface should be a code-less specification and the change would have consequences. Here is one.

By-Passing Your Interface

When I was implementing netbeans:Html4Java API, I had to create an observable list - so I did it and created JSONList. I've carefully overwritten each public method that modified the list state and called a change notification handler. What could go wrong?

@Override
public boolean add(T e) {
  boolean ret = super.add(e);
  notifyChange();
  return ret;
}

I thought I did everything correctly, as all the methods of the List interface were properly overwritten. But (as you can probably guess) a problem appeared with DefaultMethods.

The Sorting Problem

If you write this code:

People p = new People();
List<String> names = p.getNicknames(); // returns the JSONList implementation
Collections.sort(names);

it works properly on JDK7, but it gets broken on JDK8. The code needs to compile on JDK7, so no DefaultMethods (introduced in JDK8) are called. In spite of that the code behaves differently on JDK8 and the notification change isn't delivered!

The problem is that the static sort method in Collections does the sorting by itself in JDK7, but in JDK8 it delegates to List.sort:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

In addition to that there is an optimized implementation of sort in ArrayList that bypasses all existing (at time of JDK7) methods and sorts directly the internal array:

@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

as such the JSONList can be sorted without notifying about changes. None of the methods known in JDK7 is called and yet the content of the array is altered.

This is a similar problem as the one with delegation and adding new methods into existing types as discussed in Chapter 8 of TheAPIBook.

Beware of DefaultMethods

The solution is to overwrite the sort method, but this is the kind of problems we can expect with DefaultMethods - interface no longer represents a snapshot of protocol at a time, it evolve (which is what we wanted), but also with all the (sometimes unwanted) consequences.

Personal tools
buy