Written on
Groovy, Java 8 & Virtual Extension Methods
As some of my daily projects deal with Java, I thought it would be a good idea to dig into some language changes Java 8 will provide. Undoubtedly, Java 8 is well known for introducing Lambda expressions as first-class language constructs. There will be a separate blog post concerning Lambda expressions, however there are certain not so well known ingredients Java needs to provide to do a soft migration to enhance already existing classes with lambda support.Groovy: DGM
The Groovy programming language already comes with a rather advanced version of Lambda: Groovy closures. Apart from feeling like Lambda expressions, they additionally provide features to modify method delegation. This functionality has to be provided in order to allow the creation of lean DSLs, it is a key-feature for creating Groovy-based domain-specific languages. One advantage of closures being first-class language citizens is their applicability in collection classes. This is by far not the only use-case, but its one that hits core-API developers right in their face: how to modify already existing interfaces and classes without breaking already existing byte-code? Groovy did the trick by introducing the so-called "Default Groovy Methods". This term refers to a special set of static methods indirectly extending core Java classes such as java.lang.Object [0] by the all-known each method taking a groovy.lang.Closure has the first parameter.
def l = [1,2,3,4]
l.each { println it } // calls DefaultGroovyMethods.each(T, Closure)
Virtual Extension Methods
To support Lambda in already existing classes without breaking existing binary code, a new language feature has been introduced in Java 8: virtual extension methods [2][3] [4]. Let's start with Java interfaces. According to the Java language specification an interface is[An interface declaration] introduces a new reference type whose members are classes, interfaces, constants, and abstract methods. This type has no implementation, but otherwise unrelated classes can implement it by providing implementations for its abstract methods.However, with virtual extension methods, interface method declarations can specify default method implementations and therefore become so-called extended interfaces with one or more "virtual" extension methods. The term "virtual" might be confusing for those who are not quite familiar with JVM internals or the Java language specification. It refers to the terminated method invocation method during compilation. Non-private and non-super instance methods are said to be virtually invoked. Let's have a look at our first virtual extension method:
interface A {
void m() default { System.out.println("Hello, you."); }
}
class C implements A {}
C c = new C();
c.m();
interface A {
void m1() default { System.out.println("Hello, you."); }
}
interface B {
void m2() default { System.out.println(" ... "); }
}
class C implements A, B {}
C c = new C();
c.m1();
c.m2();
interface Base {
int m();
}
interface A extends Base {
int m() default { return 100; }
}
interface B extends Base {
int m() default { return 200; }
}
class C implements A, B {}
interface Base {
int m();
}
interface A extends Base {
int m() default { return 1; }
}
interface B extends Base {
int m() default { return 2; }
}
interface C extends A, B {
int m() default { return B.super.m(); }
}
class CImpl implements C {}
interface Base {
int m();
}
interface A extends Base {
int m() default { return 1; }
}
interface B extends Base {
int m() default { return 2; }
}
class C implements A, B {
public int m() { return B.super.m(); }
}
interface C extends B, A {
int m(); // <-- removes A & B's default implementation
}
class CImpl implements C {
public int m() { return 42; }
}
The Java 8 Collection Class Lambda Extensions
Now let's see how virtual extension methods look in the wild when being used in 8.0's Iterable<T> extended interface:
// ...
/**
* Returns the first element from this Iterable. Depending on the source
* type of this Iterable repeated calls may-or-may-not return the same
* element.
*
* @return an element of the collection
* @throws NoSuchElementException if this Iterable contains no elements.
*
*/
T getFirst() default {
return Iterables.getFirst(this);
}
// ...
/**
* Returns {@code true} if any of the elements match the provided predicate.
*
* @param filter a predicate against which returns {@code true} for
* matching elements.
* @return {@code true} if any elements returned {@code true} for the
* provided predicate.
*/
public boolean anyMatch(Predicate<? super T> filter) default {
return Iterables.anyMatch(this, filter);
}
// ...