$30 off During Our Annual Pro Sale. View Details »

Java 8 Lambda Expressions

Takipi
April 30, 2014

Java 8 Lambda Expressions

Java 8 introduced a change named Java's biggest ever -- lambda expressions. In this talk we'll explore lambda expressions, when and how to use them, then dive even deeper and see how they work under the hood.

Takipi

April 30, 2014
Tweet

More Decks by Takipi

Other Decks in Programming

Transcript

  1. Java 8 λs
    IL JUG, April 2014
    Niv Steingarten, Takipi

    View Slide

  2. Outline
    ● What are lambda expressions?
    ● Java 8 lambdas + Stream API
    ● Syntax
    ● Under the hood
    ● Questions

    View Slide

  3. Lambda expressions

    View Slide

  4. Lambda expressions
    Anonymous implementation
    of a
    functional interface

    View Slide

  5. Lambda expressions
    List list = Arrays.asList(
    "Say", "hello", "to", "Java 8", "Lambdas");
    Collections.sort(list, new Comparator() {
    @Override
    public int compare(String x, String y) {
    return x.compareTo(y);
    }
    });
    System.out.println(list);

    View Slide

  6. Lambda expressions
    List list = Arrays.asList(
    "Say", "hello", "to", "Java 8", "Lambdas");
    Collections.sort(list, new Comparator() {
    @Override
    public int compare(String s1, String s2) {
    return s1.compareTo(s2);
    }
    });
    System.out.println(list);

    View Slide

  7. Lambda expressions
    Java < 8
    Collections.sort(list, new Comparator() {
    @Override
    public int compare(String x, String y) {
    return x.compareTo(y);
    }
    });

    View Slide

  8. Lambda expressions
    Java < 8
    Collections.sort(list, new Comparator() {
    @Override
    public int compare(String x, String y) {
    return x.compareTo(y);
    }
    });
    Java 8
    list.sort((x, y) -> x.compareTo(y));

    View Slide

  9. Lambda expressions
    List list =
    Arrays.asList(3, 9, 12, -1);
    int factor = getFactor();
    list.forEach(x -> System.out.println(x * factor));

    View Slide

  10. When can we use λs?

    View Slide

  11. When can we use λs?
    “SAM”
    Single Abstract Method

    View Slide

  12. When can we use λs?
    @FunctionalInterface
    public interface Comparator {
    int compare(T o1, T o2);
    }

    View Slide

  13. When can we use λs?
    @FunctionalInterface
    public interface Comparator {
    int compare(T o1, T o2);
    }
    @FunctionalInterface
    public interface Predicate {
    boolean test(T t);
    }

    View Slide

  14. When can we use λs?
    Java < 8
    new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Lambdas can have");
    System.out.println("multiple statements");
    }
    }).start();

    View Slide

  15. When can we use λs?
    Java 8
    new Thread(() -> {
    System.out.println("Lambdas can have");
    System.out.println("multiple statements");
    }).start();

    View Slide

  16. Streams + Lambdas

    View Slide

  17. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    List newPrices = new ArrayList();
    for (Double p : prices) {
    System.out.println(p);
    if (p > 300) {
    newPrices.add(p * (1.0 - dailyDiscount));
    }
    }
    Collections.sort(newPrices);
    for (Double p : newPrices) {
    System.out.println(p);
    }

    View Slide

  18. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));

    View Slide

  19. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));
    Consumer:
    T → void

    View Slide

  20. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));
    Predicate:
    T → boolean

    View Slide

  21. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));
    Function:
    T → R

    View Slide

  22. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));
    Requires:
    T implements Comparable

    View Slide

  23. Streams + Lambdas
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(p -> p * (1.0 - dailyDiscount))
    .sorted()
    .forEach(p -> System.out.println(p));
    Consumer: (+ terminal operation)
    T → void

    View Slide

  24. Syntax

    View Slide

  25. Syntax
    ● (int x) -> { int y = x + 1; return y; }

    View Slide

  26. Syntax
    ● (int x) -> { int y = x + 1; return y; }
    ● (int x) -> { return x + 1; }

    View Slide

  27. Syntax
    ● (int x) -> { int y = x + 1; return y; }
    ● (int x) -> { return x + 1; }
    ● (int x) -> x + 1

    View Slide

  28. Syntax
    ● (int x) -> { int y = x + 1; return y; }
    ● (int x) -> { return x + 1; }
    ● (int x) -> x + 1
    ● (x) -> x + 1

    View Slide

  29. Syntax
    ● (int x) -> { int y = x + 1; return y; }
    ● (int x) -> { return x + 1; }
    ● (int x) -> x + 1
    ● (x) -> x + 1
    ● x -> x + 1

    View Slide

  30. Syntax
    ● (int x) -> { int y = x + 1; return y; }
    ● (int x) -> { return x + 1; }
    ● (int x) -> x + 1
    ● (x) -> x + 1
    ● x -> x + 1
    ● (x, y) -> x * y

    View Slide

  31. Off-topic:
    Method references
    List prices = Arrays.asList(1000.0, 150.0, 499.0);
    prices.stream()
    .peek(p -> System.out.println(p))
    .filter(p -> (p > 300))
    .map(Double::toString)
    .sorted()
    .forEach(p -> System.out.println(p));

    View Slide

  32. Under the hood

    View Slide

  33. Java bytecode

    View Slide

  34. Java bytecode
    ALOAD 0
    GETFIELD java/util/ArrayList.elementData : [Ljava/lang/Object;
    GETSTATIC java/util/ArrayList.EMPTY_ELEMENTDATA : [Ljava/lang/Object;
    IF_ACMPEQ L1
    ICONST_0
    GOTO L2
    BIPUSH 10
    ISTORE 2
    ILOAD 1
    ILOAD 2
    IF_ICMPLE L4
    ALOAD 0
    ILOAD 1
    INVOKESPECIAL java/util/ArrayList.ensureExplicitCapacity (I)V
    RETURN

    View Slide

  35. Java bytecode
    ● Designed for Java 1

    View Slide

  36. Java bytecode
    ● Designed for Java 1
    ● Strict Object-Oriented specification

    View Slide

  37. Java bytecode
    ● Designed for Java 1
    ● Strict Object-Oriented specification
    ● Highly-performant on modern JVMs

    View Slide

  38. Under the hood
    List list =
    Arrays.asList("Alice", "Bob", "Eve");
    list.forEach(s -> System.out.println(s));

    View Slide

  39. Under the hood
    List list =
    Arrays.asList("Alice", "Bob", "Eve");
    list.forEach(s -> System.out.println(s));

    View Slide

  40. Under the hood
    ...
    aload_0
    invokedynamic
    #0:accept:()LConsumer;
    invokeinterface
    List.forEach:(LConsumer;)V
    ...

    View Slide

  41. Under the hood
    Step 1: Desugaring

    View Slide

  42. Desugaring
    class A {
    public void foo() {
    List list = ...
    list.forEach(s -> System.out.println(s));
    }
    }

    View Slide

  43. Desugaring
    class A {
    public void foo() {
    List list = ...
    list.forEach([await link of lambda$1 Consumer]);
    }
    private static void lambda$1(String s) {
    System.out.println(s);
    }
    }

    View Slide

  44. Desugaring
    class B {
    public void foo() {
    List list = ...
    int factor = ...
    list.map(n -> n * factor);
    }
    }

    View Slide

  45. Desugaring
    class B {
    public void foo() {
    List list = ...
    int factor = ...
    list.map([await link of lambda$1 as Function]);
    }
    private static Integer lambda$1(int factor, Integer n)
    {
    return n * factor;
    }
    }

    View Slide

  46. Under the hood
    Step 2: The Lambda Metafactory

    View Slide

  47. The Lambda Metafactory
    metaFactory(MethodHandles.Lookup caller,
    String invokedName,
    MethodType invokedType,
    MethodHandle descriptor,
    MethodHandle impl)

    View Slide

  48. The Lambda Metafactory

    View Slide

  49. The Lambda Metafactory
    ● “invokedynamic” encountered

    View Slide

  50. The Lambda Metafactory
    ● “invokedynamic” encountered
    ● Metafactory is invoked once

    View Slide

  51. The Lambda Metafactory
    ● “invokedynamic” encountered
    ● Metafactory is invoked once
    ● ...returns a specific “Lambda Factory”

    View Slide

  52. The Lambda Metafactory
    ● “invokedynamic” encountered
    ● Metafactory is invoked once
    ● ...returns a specific “Lambda Factory”
    ● Call site linked to the specific factory forever

    View Slide

  53. Under the hood
    Step 3: The Lambda Factory

    View Slide

  54. The Lambda Factory
    class A {
    public void foo() {
    List list = ...
    list.forEach([lambda$1 factory]);
    }
    private static void lambda$1(String s) {
    System.out.println(s);
    }
    }

    View Slide

  55. The Lambda Factory
    class lambda$1Factory {
    public Consumer create(...) {
    ...
    }
    }

    View Slide

  56. The Lambda Factory
    class A {
    public void foo() {
    List list = ...
    list.forEach([lambda$1Factory]);
    }
    }

    View Slide

  57. Under the hood
    Step 4: The Lambda

    View Slide

  58. The Lambda
    class Lambda$1Impl implements Consumer {
    @Override
    public void accept(String s) {
    A.lambda$1bridge(s);
    }
    }

    View Slide

  59. The Lambda
    class Lambda$1Impl implements Consumer {
    @Override
    public void accept(String s) {
    A.lambda$1bridge(s);
    }
    }

    View Slide

  60. But why?

    View Slide

  61. But why?
    ● Try not to touch the bytecode specification

    View Slide

  62. But why?
    ● Try not to touch the bytecode specification
    ● Delegates lambda handling to the JVM

    View Slide

  63. But why?
    ● Try not to touch the bytecode specification
    ● Delegates lambda handling to the JVM
    ● While allowing the compiler minimal control

    View Slide

  64. But why?
    ● Try not to touch the bytecode specification
    ● Delegates lambda handling to the JVM
    ● While allowing the compiler minimal control

    View Slide

  65. But why?
    ● Try not to touch the bytecode specification
    ● Delegates lambda handling to the JVM
    ● While allowing the compiler minimal control
    ● Enabled runtime optimization and analysis

    View Slide

  66. But why?
    ● Try not to touch the bytecode specification
    ● Delegates lambda handling to the JVM
    ● While allowing the compiler minimal control
    ● Enabled runtime optimization and analysis
    ● Allow flexibility in internal implementation

    View Slide

  67. Thank you!
    Now go and be awesome.
    [email protected]
    @nivstein / @takipid

    View Slide