Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Java 8 for Android

Introduction to Java 8 for Android

A brief introduction to Java 8 features and their implementation for Android

G. Blake Meike

November 30, 2016
Tweet

More Decks by G. Blake Meike

Other Decks in Programming

Transcript

  1. What’s New? • Type Annotations • Functional Interfaces • Lambda

    Expressions • Method References • Default Methods • Streams
  2. Type Annotations public void fn( @NonNull List<@NonNull String> arg) Annotations

    can now be used anywhere a type can be specified. For instance, with the CheckerFramework:
  3. Introducing Functional Interfaces The heart of Java 8 A compromise

    completely brilliant in its utter lack of audacity. @FunctionalInterface
 interface Runnable {
 void run();
 }
  4. Functional Interfaces • Similar to old SAM (Single Abstract Method)

    Interfaces • Must be an interface: An abstract class cannot be a Functional Interface • Verified with the @FunctionalInterface annotation
  5. Lambda Expressions An anonymous function: Syntax for creating a function

    not bound to an identifier (arg1, … argn) -> { statement; …; return v; } Prior to Java 8, there was no way to do this in Java … and there still isn't!
  6. Lambda Syntax In Java, a lambda expression is syntactic sugar

    for an anonymous instance of a Functional Interface that implements the Interface's single abstract method. (arg1, … argn) -> { statement; statement; …; return v; }
  7. Lambda Syntax (arg1, … argn) -> { statement; statement; …;

    return v; } … is, essentially: new FI() { public V f(arg1, … argn){ statement; statement; …; return v; } } Do NOT forget that lambdas are NOT references to functions!
  8. Lambda Semantics All exits from a lambda with a return

    type must be return statements with a value of that type. (a1, a2) -> { if (a1.equals(a2)) { return a1; }
 else { return a2; }};
  9. Lambda Semantics A lambda with a void return type may

    end with a statement or exit with an empty return statement. (a1, a2) -> { if (a1 == null) { return; }
 a1.set(a2); };
  10. Lambda Shortcuts (p1, p2, p3, ...) -> expression; If the

    body of the lambda is a single statement or expression, the {} and return may be elided
  11. Lambda Shortcuts p -> expression; If there is only one

    argument, the parenthesis may be elided:
  12. Method References For those of you that find lambdas excessively

    verbose: • static: Class::staticMethod • constructor: Class::new • bound instance: instance::instanceMethod • unbound instance: Class::instanceMethod
  13. Method References Static … is the same as: SomeClass::fn (fn

    is a static method) x -> SomeClass.fn(x) List<String> slist = ilist.map(String::valueOf);
  14. Method References Constructor SomeClass::new (new is a constructor) x ->

    new SomeClass(x) … is the same as: List<String> slist = ilist.map(String::new);
  15. Method References Instance … is the same as: this::fn (fn

    is an instance method) x -> this.fn(x) List<String> slist = ilist.map(this::asString);
  16. Method References Instance … is the same as: ClassOfX::fn (fn

    is an instance method) (x, y) -> x.fn(y)
  17. Lambda are not References to Functions! Function<Integer, Integer> fn =

    x -> x + x; // Compilation error: Method call expected int z = fn(3); // Compilation error: operator ‘+’ cannot… int y = (x -> x + x)(1); int y = (x -> x + x).apply(1);
  18. A Lambda is an Instance A lambda is an anonymous

    instance of a Functional Interface. You must call its single abstract method. @FunctionalInterface
 public interface Fn { void p(String arg); }
 
 Fn fn = s -> System.out.println(s); fn.p("Hello");
  19. Abstract Semantics Suppose we have some code: public static void

    main(String... args) {
 // code...
 } … that we want to replace with a lambda public static void main(String... args) {
 Task<I, O> task = in -> {
 // code...
 };
 
 new Thread(() -> { task.run(in); }).start();
 }
  20. Lambda Limitations Note: The BGGA proposal for lambdas was not

    subject to these constraints http://www.javac.info/closures-v05.html • captured non-final variable • a reference to this • break and continue statements • a return statement • throwing an exception
  21. Effectively Final Captured variables no longer need to be declared

    final. … but you better not try to change them! String[] args = new String[]{"one", "two"};
 
 int i = 0;
 Predicate<String> pred = x -> x.equals(args[i]);
 
 System.out.println(pred.test("one")); // true
 
 args[0] = "three"; // ok!!
 System.out.println(pred.test("one")); // false
 
 i = 1; // Causes compilation error up there ^^
  22. this It is no longer necessary to say: OuterClass.this. private

    int x;
 private Predicate<Integer> p2 = new Predicate<Integer>() {
 @Override
 public boolean test(Integer n) {
 return n == OuterClass.this.x;
 }
 };
 private Predicate<Integer> p1 = n -> n == x;
  23. this On the other hand, other members defined in the

    interface are invisible! public interface Test extends Predicate<String> {
 // inaccessable from lambdas !!!
 String hello = "hello";
 
 // inaccessable from lambdas !!!
 default String getHello() { return hello; }
 
 boolean test(String arg);
 }
  24. Lambda Typing It is the context, not the lambda, that

    determines type @FunctionalInterface
 public interface F1 { String f(String n); }
 @FunctionalInterface
 public interface F2 { Integer f(Integer n); }
 
 F1 fn1 = x -> x + x;
 F2 fn2 = x -> x + x;
 
 fn1.f(2); // Compilation error: Fn1 cannot be applied to int
 fn1.f("a"); // "aa" fn2.f(2); // 4
 fn2.f("a"); // Compilation error: Fn2 cannot be applied to String
  25. Lambda Typing A lambda doesn’t have an intrinsic type. Its

    type must be inferred from context. // Compilation error: cannot resolve getClass
 (x -> x).getClass().getCanonicalName();
 
 // Compilation error: // cannot cast <lambda parameter> as Object
 x -> (Object) x;
  26. Lambda Types: Functional Interface @FunctionalInterface
 public interface Runnable { void

    run(); }
 
 @FunctionalInterface
 public interface Callable<V> {
 V call() throws Exception;
 } Some old friends:
  27. Powerful New Types • Consumer - void accept(T v) •

    BiConsumer - void accept(T t, U u) • Function - R apply(T v) • Operator - extends Function<T, T> • Predicate - boolean test(T t) • Supplier - T get()
  28. Higher Order Functions In a first-order calculus functions take variables

    as arguments In higher-order logic, functions may take other functions as arguments.
  29. The Collection Library A very common place in which to

    use higher-order techniques is collections. <R, T> List<R> map(
 Function<? super T, ? extends R> f);
 
 <R, T> List<R> flatMap(
 Function<? super T, ? extends List<? extends R>> f);
  30. The Brittle Interface Problem Change the interface: public interface API

    {
 void doSomething();
 void doSomethingElse(); //added
 } //Compilation error: // must implement doSomethingElse public class Server implements API {
 public void doSomething() { }
 }; … and implementations no longer compile
  31. Introducing Default Methods public interface Callback {
 void onEvent(Event e);


    
 default void onEventWithContext(
 Event e, Context txt)
 {
 onEvent(e);
 }
 }
  32. Default Methods Adding a default method makes the interface backwards

    compatible: public interface API {
 void doSomething();
 default void doSomethingElse() {
 // …
 }
 }
  33. Default Method Details: Combining A default method in one interface

    will not satisfy a requirement from another interface: public interface I1 { void F(); }
 public interface I2 {
 default void F(){ … }
 }
 
 // Compilation error: F undefined
 public class C implements I1, I2 { … }
  34. Default Methods Details: Multiple Inheritance Multiple inheritance is a compile

    time error: public interface I1 {
 default void F() {…}
 }
 public interface I2 {
 default void F() {…}
 }
 
 // Compilation error: multiple inheritance
 public class C implements I1, I2 { … }

  35. Default Methods Details: Overriding A default method overrides a definition

    in a super interface: A call to C.f() will print the string “I2” public interface I1 {
 default void f() {
 System.out.println(”I1”)
 }
 }
 public interface I2 extends I1 {
 default void f() {
 System.out.println(”I2”)
 }
 }
 public class C implements I2 { … }
  36. Default Methods Details: super Like "this", "super" doesn't work in

    a default method public interface I1 {
 default void f() {
 System.out.println(”I1”)
 }
 } 
 public interface I2 extends I1 {
 default void f() {
 // Compilation error: // cannot resolve method f super.f(); }
 }
  37. Collections Default methods make it possible to add functional behavior

    to, e.g., the Collections API public interface Collection<E> {
 // … API as it has been since Java 1.2
 
 default Stream<E> parallelStream() { … }
 default Spliterator<E> spliterator() { … }
 default Stream<E> stream() { … }
 }
  38. Creating Streams • Stream.empty • Stream.of(values) • Collection.stream() • Stream.iterate(seed,

    UniaryOp) • Stream.generate(Supplier) • Stream.concat(Stream, Stream)
  39. Using Streams Stream s = Stream.of("hello", "world");
 s.forEach(System.out::println);
 // Runtime

    error: IllegalStateException
 s.forEach(System.out::println); Don’t re-use them.
  40. Using Streams Stream s = Stream.iterate("a", c -> c +

    "a");
 s.forEach(System.out::println); Don’t evaluate an infinite stream! Stream s = Stream.iterate("a", c -> c + "a");
 s.distinct()
 .limit(10)
 .forEach(System.out::println); Really! Don’t evaluate an infinite stream!
  41. Using Streams Stream s = Stream.iterate("a", c -> c +

    "a");
 s.limit(10)
 .distinct()
 .forEach(System.out::println); Order matters!
  42. Collecting Streams • Collectors.averaging 㱺 int, long, double • Collectors.counting

    㱺 long • Collectors.joining 㱺 String • Collectors.groupingBy 㱺 Map • Collectors.toList 㱺 Map • many more…
  43. RetroLambda buildscript {
 repositories {
 jcenter()
 mavenCentral()
 }
 dependencies {


    classpath 'com.android.tools.build:gradle:2.1.2'
 classpath ‘me.tatarka:gradle-retrolambda:3.4.0'
 }
 }
  44. RetroLambda apply plugin: 'com.android.application'
 apply plugin: 'me.tatarka.retrolambda'
 
 android {


    // …
 
 compileOptions {
 sourceCompatibility JavaVersion.VERSION_1_8
 targetCompatibility JavaVersion.VERSION_1_8
 } // … } dependencies {
 retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
 // … }
  45. Jack apply plugin: 'com.android.application'
 android {
 // … defaultConfig {


    minSdkVersion 24
 targetSdkVersion 24 
 // … jackOptions { enabled true }
 }
 compileOptions {
 sourceCompatibility JavaVersion.VERSION_1_8
 targetCompatibility JavaVersion.VERSION_1_8
 } // … }
  46. Jack • Most of the Java 8 API Hot Not

    • Only works for API target >=24 • Breaks on libraries
  47. Implementing Lambdas Java 8 implements Lambdas with the new invoke-dynamic

    instruction. Dalvik/Art has no such instruction
  48. But first… public class Lambda { Func<Integer> fn; public void

    test() { fn = new Func<Integer>() { public Integer apply(Integer x) { return x + x; } }; } }
  49. Class Lambda public void test(); 0: aload_0 1: new #2

    // class Lambda$1 4: dup 5: aload_0 6: invokespecial #3 // Method Lambda$1."<init>":(LLambda;)V 9: putfield #4 // Field fn:LFunc; 12: return
  50. Inner Class Lambda$1 public <init> 0: aload_0 // Hidden reference!

    1: aload_1 2: putfield #1 // Field this$0:LLambda; 5: aload_0 6: invokespecial #2 // Method java/lang/Object."<init>":()V 9: return public java.lang.Integer apply(java.lang.Integer); 0: aload_1 1: invokevirtual #3 // Method java/lang/Integer.intValue:()I 4: aload_1 5: invokevirtual #3 // Method java/lang/Integer.intValue:()I 8: iadd 9: invokestatic #4 // Method java/lang/Integer.valueOf: // (I)Ljava/lang/Integer; 12: areturn public java.lang.Object apply(java.lang.Object); // raw type // !!! ...
  51. Java: lambda creation invoke dynamic public void test(); 0: aload_0

    1: invokedynamic #2, 0 // InvokeDynamic #0:apply:()LFunc; 6: putfield #3 // Field fn:LFunc; 9: return 0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(…)… #25 (Ljava/lang/Object;)Ljava/lang/Object; // actual type #26 invokestatic Lambda.lambda$test$0: // implementation (Ljava/lang/Integer;)Ljava/lang/Integer; #27 (Ljava/lang/Integer;)Ljava/lang/Integer; // enforced type
  52. Java: lambda execution private static java.lang.Integer lambda$test$0(java.lang.Integer); 0: aload_0 1:

    invokevirtual #4 // Method java/lang/Integer.intValue:()I 4: aload_0 5: invokevirtual #4 // Method java/lang/Integer.intValue:()I 8: iadd 9: invokestatic #5 // Method java/lang/Integer.valueOf: // (I)Ljava/lang/Integer; 12: areturn
  53. Retrolambda Class Lambda public void test(); 0: aload_0 1: invokestatic

    #21 // Method Lambda$$Lambda$1. // lambdaFactory$:()LFunc; 7: return static java.lang.Integer lambda$test$0(java.lang.Integer); 0: aload_0 1: invokevirtual #31 // Method java/lang/Integer.intValue:()I 4: aload_0 5: invokevirtual #31 // Method java/lang/Integer.intValue:()I 8: iadd 9: invokestatic #35 // Method java/lang/Integer. // valueOf:(I)Ljava/lang/Integer; 12: areturn
  54. Retrolambda Class Lambda$Lambda$1 static {}; 0: new #2 // class

    Lambda$$Lambda$1 3: dup 4: invokespecial #25 // Method "<init>":()V 7: putstatic #27 // Field instance:LLambda$$Lambda$1; 10: return public static Func lambdaFactory$(); 0: getstatic #27 // Field instance:LLambda$$Lambda$1; 3: areturn public java.lang.Object apply(java.lang.Object); 0: aload_1 1: checkcast #15 // class java/lang/Integer 4: invokestatic #21 // Method Lambda.lambda$test$0: // (Ljava/lang/Integer;)Ljava/lang/Integer; 7: areturn
  55. Jack Class Lambda public test()V new-instance v0, L-$Lambda$0; invoke-direct {v0},

    L-$Lambda$0;-><init>()V iput-object v0, p0, LLambda;->fn:LFunc; return-void
  56. Jack Class -$Lambda$0 public apply(Ljava/lang/Object;)Ljava/lang/Object; invoke-direct {p0, p1}, L-$Lambda$0;->$m$0(Ljava/lang/Object;)Ljava/lang/Object; move-result-object

    v0 return-object v0 private $m$0(Ljava/lang/Object;)Ljava/lang/Object; check-cast p1, Ljava/lang/Integer; invoke-static {p1}, LLambda;->lambda$-Lambda_lambda$1(Ljava/lang/Integer;)Ljava/lang/Integer; move-result-object v0 return-object v0 static lambda$-Lambda_lambda$1(Ljava/lang/Integer;)Ljava/lang/Integer; invoke-virtual {p0}, Ljava/lang/Integer;->intValue()I move-result v0 invoke-virtual {p0}, Ljava/lang/Integer;->intValue()I move-result v1 add-int/2addr v0, v1 invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; move-result-object v0 return-object v0
  57. Java 8 on Android? • Retrolambda for backward compatibility •

    Jack will support most of Java 8 in API 24 • Lambdas • Streams • Jack is probably not prime-time yet • Lambdas aren't leak-proof!
  58. Thank you! [email protected] twitter: @callmeike I will be signing Android

    Concurrency at the BZMedia booth, at 5:15pm TODAY!