Instantly share code, notes, and snippets.

Embed
What would you like to do?
A comparison between non-capturing and capturing expressions across Java 6, Java 8, Java 8 with Retrolambda, Kotlin with native function expressions, and Kotlin with Java SAM expression.
Each respective use has a .class method cost of
Java 6 anonymous class: 2
Java 8 lambda: 1
Java 8 lambda with Retrolambda: 6
Java 8 method reference: 0
Java 8 method reference with Retrolambda: 5
Kotlin with Java Runnable expression: 3 (subtract one for non-capturing)
Kotlin with native function expression: 4 (subtract one for non-capturing)
All of them also require an additinal class to be generated (Java 8 defers this to happen at runtime).
(Remember, don't count the methods from `Capturing` / `NonCapturing` or its implicit constructor)
import java.util.Arrays;
class NonCapturing {
public static void main(String... args) {
run(new Runnable() {
@Override public void run() {
System.out.println("Hey!");
}
});
}
private static void run(Runnable run) {
run.run();
}
}
class Capturing {
public static void main(final String... args) {
run(new Runnable() {
@Override public void run() {
System.out.println("Hey! " + Arrays.toString(args));
}
});
}
private static void run(Runnable run) {
run.run();
}
}
$ javap -p NonCapturing*
Compiled from "Java6.java"
final class NonCapturing$1 implements java.lang.Runnable {
NonCapturing$1();
public void run();
}
Compiled from "Java6.java"
class NonCapturing {
NonCapturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
}
$ javap -p Capturing*
Compiled from "Java6.java"
final class Capturing$1 implements java.lang.Runnable {
final java.lang.String[] val$args;
Capturing$1(java.lang.String[]);
public void run();
}
Compiled from "Java6.java"
class Capturing {
Capturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
}
import java.util.Arrays;
class NonCapturing {
public static void main(String... args) {
run(() -> System.out.println("Hey!"));
}
private static void run(Runnable run) {
run.run();
}
}
class Capturing {
public static void main(String... args) {
run(() -> System.out.println("Hey! " + Arrays.toString(args)));
}
private static void run(Runnable run) {
run.run();
}
}
$ javap -p NonCapturing*
Compiled from "Java8Lambda.java"
class NonCapturing {
NonCapturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
private static void lambda$main$0();
}
$ javap -p Capturing*
Compiled from "Java8Lambda.java"
class Capturing {
Capturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
private static void lambda$main$1(java.lang.String[]);
}
$ javap -p NonCapturing*
final class NonCapturing$$Lambda$1 implements java.lang.Runnable {
private static final NonCapturing$$Lambda$1 instance;
private NonCapturing$$Lambda$1();
public void run();
static {};
public static java.lang.Runnable lambdaFactory$();
}
Compiled from "Java8Lambda.java"
class NonCapturing {
NonCapturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
private static void lambda$main$0();
static void access$lambda$0();
}
$ javap -p Capturing*
final class Capturing$$Lambda$1 implements java.lang.Runnable {
private final java.lang.String[] arg$1;
private Capturing$$Lambda$1(java.lang.String[]);
private static java.lang.Runnable get$Lambda(java.lang.String[]);
public void run();
public static java.lang.Runnable lambdaFactory$(java.lang.String[]);
}
Compiled from "Java8Lambda.java"
class Capturing {
Capturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
private static void lambda$main$1(java.lang.String[]);
static void access$lambda$0(java.lang.String[]);
}
class NonCapturing {
public static void main(String... args) {
run(NonCapturing::sayHi);
}
private static void run(Runnable run) {
run.run();
}
private static void sayHi() {
System.out.println("Hey!");
}
}
class Capturing {
public static void main(Capturing instance) {
run(instance::sayHi);
}
private static void run(Runnable run) {
run.run();
}
void sayHi() {
System.out.println("Hey!");
}
}
$ javap -p NonCapturing*
Compiled from "Java8MethodRef.java"
class NonCapturing {
NonCapturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
private static void sayHi();
}
$ javap -p Capturing*
Compiled from "Java8MethodRef.java"
class Capturing {
Capturing();
public static void main(Capturing);
private static void run(java.lang.Runnable);
void sayHi();
}
$ javap -p NonCapturing*
final class NonCapturing$$Lambda$1 implements java.lang.Runnable {
private static final NonCapturing$$Lambda$1 instance;
private NonCapturing$$Lambda$1();
public void run();
static {};
public static java.lang.Runnable lambdaFactory$();
}
Compiled from "Java8MethodRef.java"
class NonCapturing {
NonCapturing();
public static void main(java.lang.String...);
private static void run(java.lang.Runnable);
static void sayHi();
static void access$lambda$0();
}
$ javap -p Capturing*
final class Capturing$$Lambda$1 implements java.lang.Runnable {
private final Capturing arg$1;
private Capturing$$Lambda$1(Capturing);
private static java.lang.Runnable get$Lambda(Capturing);
public void run();
public static java.lang.Runnable lambdaFactory$(Capturing);
}
Compiled from "Java8MethodRef.java"
class Capturing {
Capturing();
public static void main(Capturing);
private static void run(java.lang.Runnable);
void sayHi();
static void access$lambda$0(Capturing);
}
// 'run' is built-in method so we use 'run2' instead.
class NonCapturing {
fun main(vararg args: String) {
run2(Runnable { println("Hey!") })
}
private fun run2(func: Runnable) {
func.run()
}
}
class Capturing {
fun main(vararg args: String) {
run2(Runnable { println("Hey! $args") })
}
private fun run2(func: Runnable) {
func.run()
}
}
$ javap -p NonCapturing*
Compiled from "KotlinClass.kt"
final class NonCapturing$main$1 implements java.lang.Runnable {
public static final NonCapturing$main$1 INSTANCE;
public final void run();
NonCapturing$main$1();
static {};
}
Compiled from "KotlinClass.kt"
public final class NonCapturing {
public final void main(java.lang.String...);
private final void run2(java.lang.Runnable);
public NonCapturing();
}
$ javap -p Capturing*
Compiled from "KotlinClass.kt"
final class Capturing$main$1 implements java.lang.Runnable {
final java.lang.String[] $args;
public final void run();
Capturing$main$1(java.lang.String[]);
}
Compiled from "KotlinClass.kt"
public final class Capturing {
public final void main(java.lang.String...);
private final void run2(java.lang.Runnable);
public Capturing();
}
// 'run' is built-in method so we use 'run2' instead.
class NonCapturing {
fun main(vararg args: String) {
run2({ println("Hey!") })
}
private fun run2(func: () -> Unit) {
func()
}
}
class Capturing {
fun main(vararg args: String) {
run2({ println("Hey! $args") })
}
private fun run2(func: () -> Unit) {
func()
}
}
$ javap -p NonCapturing*
Compiled from "KotlinFunc.kt"
final class NonCapturing$main$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<kotlin.Unit> {
public static final Capturing$main$1 INSTANCE;
public java.lang.Object invoke();
public final void invoke();
NonCapturing$main$1();
static {};
}
Compiled from "KotlinFunc.kt"
public final class NonCapturing {
public final void main(java.lang.String...);
private final void run2(kotlin.jvm.functions.Function0<kotlin.Unit>);
public NonCapturing();
}
$ javap -p Capturing*
Compiled from "KotlinFunc.kt"
final class Capturing$main$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<kotlin.Unit> {
final java.lang.String[] $args;
public java.lang.Object invoke();
public final void invoke();
Capturing$main$1(java.lang.String[]);
}
Compiled from "KotlinFunc.kt"
public final class Capturing {
public final void main(java.lang.String...);
private final void run2(kotlin.jvm.functions.Function0<kotlin.Unit>);
public Capturing();
}
@wiibaa

This comment has been minimized.

wiibaa commented Jan 8, 2016

Sorry for the silly question, but did you want to say "Java Seven with Retrolambda" ?

@JakeWharton

This comment has been minimized.

Owner

JakeWharton commented Jan 12, 2016

@wiibaa Not really. It's code compiled with Java 8 that is then run through the Retrolambda tool. I could have been more clear here, I suppose, but it's Java 8 code not Java 7 code even though the bytecode ends up not being Java 8 but Java 7 compatible.

@tomquist

This comment has been minimized.

tomquist commented Nov 23, 2016

Great summary. It would be nice to have some up to date numbers for Retrolambda 2.3 (after merging your pull request luontola/retrolambda#81).

@mattijsf

This comment has been minimized.

mattijsf commented Apr 4, 2017

Is there a difference between compiling against different minSdkVersion using the new java 8 toolchain e.g. on minSdkVersion < 24 and minSdkVersion >= 24. As on minSdkVersion 24 java 8 feature support seems much more complete, there might also be a difference in generated classes.

@piotrgwiazda

This comment has been minimized.

piotrgwiazda commented May 31, 2017

A Scala example would be great plus Kotlin 1.1 with -jvm-target 1.8

@timemanx

This comment has been minimized.

timemanx commented Jul 3, 2017

@JakeWharton In KotlinFunc_output.txt, on line 4, I guess it should be public static final NonCapturing$main$1 INSTANCE; instead of public static final Capturing$main$1 INSTANCE;.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment