Virtual extension methods -- a strawman design

Reinier Zwitserloot reinier at zwitserloot.com
Mon May 17 04:07:17 PDT 2010


Many things would be gained, Gernot, and they come in two batches. The first
batch is based on the notational difference between:

Collections.sort(someList);

and:

someList.sort();



Advantage #1: someList.sort() is more consistent with java. When you want to
do a verb to a noun you write noun.verb() in java, not
RandomAndMostlyIrrelevantGroupingNoun.verb(noun);. In other words, it's
prettier. There's an argument to be made that something like
someList.set(idx, newVal); is an "intrinsic", "basic", "atomic", "core"
(pick your preferred word :P) operation that cannot be expressed as a series
of applications of other core/atomic/basic/instrinsic operations, whereas
Collections.sort(list) is an extrinsic operation, and knowing the difference
is nice.

But this simply doesn't hold up, for two reasons: First of all, many
existing extrinsic operations are defined in interfaces anyway. isEmpty() is
extrinsic; it is almost always defined as size() != 0, and yet its in the
interface. We can't very well REMOVE isEmpty() so this hypothetical land
where intrinsic ops are of the noun.verb() variety and extrinsic ops are of
the GroupingNoun.verb(noun) variety isn't true now and never will be true.
Secondly, in many cases there's a set of operations which are mutually
extrinsic, or where the line is blurry. For example, "List.set" can
technically be considered extrinsic; after all, you could implement it with
a combination of remove(int) and add(int, T). However, "set" works in the
object returned by Arrays.asList, but remove and add don't, so how extrinsic
is List.set? Why should we even ask developers to pick? Better to treat
intrinsic/extrinsic as an ambiguous and mostly irrelevant detail that
needn't be made so obvious.

Advantage #2: In order to know what operations are possible on a given
object, you check its methods. Whether you check the javadoc or just your
IDE's auto-complete dialog, that's what you do. You cannot discover static
helpers this way. Attempting to make your API easy to learn and work with is
_far_ more difficult if that API includes a helper class with static
methods, like Collections.


Advantage #3 stems from Goetz's proposal's ability to let subtypes override
implementations. For example, As Remi Forax has shown, the obvious
implementation of a filter method, whether you use Collections.filter(list,
filterFunction) or list.filterFunction(), is to iterate through it and call
Iterator.remove() when the filter condition returns false. However, for
certain collection implementations, there are more efficient ways to do it.
If you write Collections.filter(list, filterFunction) then the static filter
method in Collections must contain a bunch of "if list instanceof"
statements to pick and choose an efficient implementation, and there's no
way to define optimized or fixed behaviour in the subtype. But with the
Goetz proposal you can write exceptional code in either location.

--Reinier Zwitserloot



On Mon, May 17, 2010 at 9:47 AM, Gernot Neppert <mcnepp02 at googlemail.com>wrote:

> I find the notion of "default implementations of interface methods" a
> little strange.
> Isn't that an oxymoron?
>
> In my understanding, an interface declares the set of methods that a
> are needed to implement the functionality that the interface was
> designed to provide. If any one method can be implemented by means of
> calling other interface methods, then it is strictly speaking not
> necessary at all. *)
>
> So, if one comes up with a new method for an existing interface, I see
> two possibilities:
>
> 1. The method can be expressed by invoking other methods. Then, why
> not simply do that? I can see nothing bad about
> "Collections.sort(List<?)" or similar static helpers.
>
> 2. The method is "original" in its declared functionality. Thus, it
> can not be expressed by invoking existing methods. Thus, how can you
> provide a default implementation?
>
>
> Here's this reasoning applied to an example that pops up frequently
> when extension methods are mentioned:
> A "filter" method on Collections.
>
> 1.hypothetical method declaration (modifying operation):
>
> /**
> * Filters this collection.
> * Removes all elements from this collection that do not satisfy the
> condition.
> * @return true if the collection changed due to the filtering.
> */
> public boolean filter(boolean #condition(E));
>
> This method can be easily implemented "externally", i.e. by means of
> iterating over  the collection and removing elements.
> What would be gained by making this an interface method?
>
> 2.hypothetical method declaration (non-modifying operation):
>
> /**
> * Returns the filtered content.
> * Returns a collection of the same type as this collection that
> contains only those elements
> that satisfy the condition.
> * @return the filtered collection.
> */
> public Collection<E> filter(boolean #condition(E));
>
> This method cannot be implemented "externally" because of the
> necessity to create a collection of the same type.
> I see no reasonable way to provide a default implementation!
>
>
>
>
>
>
>
>
> *) And yes, I do think that java.util.Collection is already bloated.
> Methods such as "addAll" do not belong there!
>
>


More information about the lambda-dev mailing list