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

Avatar for G. Blake Meike

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!