Virtual extension methods -- a strawman design

Alex Blewitt alex.blewitt at gmail.com
Sun May 23 08:03:15 PDT 2010


On 17 May 2010, at 15:14, Rémi Forax wrote:

> Le 17/05/2010 15:58, Brian Goetz a écrit :
>>> and have all JDK List-classes implement List2 instead of only List.
>>> They encourage the use of List2 in the API Doc.
>>> 
>>> There would have been no compatibility issues at all. Programmers
>>> would have adopted List2 whenenver they were writing new code.
>>> 
>> And in user code, people would have inserted casts to List2 whenever they
>> encountered a List, sometimes with an instanceof check first, sometimes
>> without.  That would not be good for the readability or maintainability of
>> Java code.
>> 
> 
> Eclipse developers do something similar.
> I was not happy when I tried to understand ITextViewer6:
> http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/text/ITextViewerExtension6.html
> 
> I am not a big fan of having to understand all the history of
> an API before being able to use it. A friend of mine calls that: 
> archeological programming.

One doesn't have to understand history to be able to use ITextViewer6; one can just use methods defined therein.

There's a distinction between an interface which is indented to be used by clients, and one which is intended to be implemented in clients. The former case permits the addition of methods; the latter does not. The evolution by I..n interfaces permits both, albeit somewhat ugly.

However, I have concerns with the defender methods in more general terms. Putting an alias to a static method is akin to permitting code in an interface; what happens in the following case:

public interface A {
  extension int stuff() default AA.stuff;
}
public interface B {
  extension int stuff() default BB.stuff;
}

public class AB implements A, B {
  // where does AB.stuff() go?
}

This is the classic 'diamond problem' that is experienced with multiple implementation inheritance. Unfortunately, the proposal document (http://cr.openjdk.java.net/~darcy/DefenderMethods.pdf) brushes this under the covers (5.1) with a 'should throw an exception' at this point. This is pretty silent, outside of any compiler warnings, and is definitely a change of behaviour.

Even without that, I don't see that any consideration has been given to what happens to already existing methods which are used prior to this change. For example, consider the lowly 'filter' method - some implementations may perform an in-place filter, returning 'true' or 'false' to indicate if any changes were allowed, whereas others may return a new copy of the data structure with elements that match the filter. What if a generic 'filter' method is then defaulted to the interface?


public A implements DataStructure {
  public boolean filter(FilterSam filter) {...}
}
public B implements DataStructure {
  public DataStructure filter(FilterSam filter) {...}
}
// Later, someone makes the change:
public interface DataStructure {
  extension DataStructure filter(FilterSam filter) default Default.filter;
}

Now we have a situation where previously there was no common filter() signature, there is now. The problem is that class A may well have been written long before the existence of B, but we have now introduced a situation where client code invoking A.filter() is presented with the return value of a different type to which it expects. 

The key problem is that the draft spec has no observations of what should happen when the extension method is of a different signature (i.e. not covariant in the return types). 

It seems to me that the first round of changes to the Collections classes are going to be unaffected by this concern purely because there is no super lambda type for existing methods to have already implemented. 

Alex


More information about the lambda-dev mailing list