I was referring to a compile-time generation of the class -- that the Closure
itself that is normally generated implements the interface natively. That would
make it equivalent to anonymous class in Java 7 and earlier for calling
functional (or any SAM type) methods. That wouldn't have any problems on
Android, and should be as efficient as Java without lambdas? I would assume the
interface's method delegating to doCall would get inlined. In other words,
Groovy generating code like:
class X {
public static void main(String[] args) {
Stream.of(1).forEach(new x__closure1(X.class, X.class));
}
private static class x__closure1 extends Closure<Void> implements
Consumer<Integer> {
public x__closure1(Object owner, Object thisObject) {
super(owner, thisObject);
}
void doCall(Integer x) {
System.out.println(x);
}
@Override
public void accept(Integer x) {
doCall(x);
}
}
}
>From Groovy: Stream.of(1).forEach { println it }
The new part being that Groovy added the accept method and implements to the
closure it already normally would have generated, and castToType would not need
to be called. All of the code manipulation is done at compile-time so it is
fully STC and Android compatible, and no reflection is in use. You still have
the a little more overhead of Closure object compared to Java static inner
class, but I imagine this must be a lot less than proxy, but still allows
Closure to use the owner/delegate patterns that Groovy is known for, and I
assume would not affect backwards compatibility as superclass stays Closure.
Of course, if it were possible for compiler to determine that the closure is
never using owner, delegate, or "thisObject", then it could be possible to drop
the "extends Closure" entirely if it can be proven that the "closureness" of
the object can never be observed. But that's likely not possible as any method
taking an interface could choose to check for instanceof Closure and/or cast or
do something special if Groovy closure is passed in -- although is that even
possible today since Groovy actually passes in a proxy?
Jason
-----Original Message-----
From: Jochen Theodorou [mailto:[email protected]]
Sent: Tuesday, November 22, 2016 12:01 PM
To: [email protected]
Subject: Re: JDK8 Streams / Closure cast to interface
On 22.11.2016 15:14, Winnebeck, Jason wrote:
> I love Groovy. I also love the new streams functionality in JDK 8.
> But, I am weary of the performance implications of Groovy + Streams,
> because to use streams you must use Groovy closures. I see the code
> generated creates a new closure instance then uses castToType to cast
> the closure to the JDK8 functionality interface. Does Groovy make this
> efficient, or is the proxy generation from this going to be excessive
> if running a lot of small stream operations?
it is using a dynamic proxy with some optimized code paths.. such a proxy does
for example not have to go through call to get to doCall. If you want this
really efficient, you have to skip the generation of the Groovy Closure
instance. This is doable and I plan to do this, but it will be a breaking
change (all open blocks would be realized using the same class for example,
having that instance would even become optional).
> I think an interesting feature of Groovy would be if it sees a closure
> cast implicitly or explicitly to a certain interface type, it could
> make the closure implement the interface. You'd still have the
> overhead of compile-time class generation versus lambdas, but at least
> you wouldn't have to create proxies, and maybe there is an improved
> chance of JIT inlining? Even if it was supported within a single
> statement, like "Function<String, String> x = {it.trim()}" or
> Stream.of("abc").map {
> it.trim() }, the closure with trim could implement Function. Of course
> for backwards compatibility the class could still extend Closure and
> still implement call methods, but also implement the method apply,
> which delegates to call.
The price question is now... is that going to be cheaper? runtime class
generation is much more expensive than a reflective call - and considering
Android, also much more problematic. And you have to be aware of the
following... If I make a method call with an open block to a method taking a
functional interface, the point in time for the conversion is after runtime
method selection and before the actual invocation (or during invocation). At
this point the original Closure instance already exists. Really, without test
it is difficult to tell about the gain here
Real gains you get only with a much deeper integration
bye Jochen
This email message and any attachments are for the sole use of the intended
recipient(s). Any unauthorized review, use, disclosure or distribution is
prohibited. If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message and any attachments.