morristech
5/10/2018 - 8:35 AM

A comparison between non-capturing and capturing expressions across Java 6, Java 8, Java 8 with Retrolambda, Kotlin with native function exp

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.

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();
}
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)