Java 8 Lambdas!

Meg

  • Narve Sætre
  • Knowit AS
  • mrnarve@gmail.com

Plan

  • Hva er egentlig lambdas og closures?
  • Syntaks
  • Relaterte språkendringer:
    • Effectively final
    • Virtual extension methods
    • Method references
  • API - Collections etc

Closures

en funksjon eller en funksjonsreferanse med et tilhørende "miljø" definert utenfor selve funksjonen


Condition stopCondition( final Server server ) {
    return new Condition() {
        public boolean evaluate() {
            return server.isRunning(); 
        }
    }
}
					

Java har altså støttet en form for closures lenge (siden 1.1): Funksjoner som refererer til variabler som ikke er en del av parameterlisten (og ikke er globalt).

Lambda

en funksjon som er definert (og invokert) uten å være bundet til en identifier

Java har tidligere hatt anonyme klasser men ikke anonyme funksjoner - mao ikke lambdaer. Java 8 har altså støtte for både lambdaer og closures, og noen lambdaer vil være closures og noen closures vil være lambdaer.

Eksempler


Runnable r = () -> {}; 
    
ActionListener al = (a) -> { log.debug( "Got action: " + a )}; 

Comparator<String> comp = (String s1, String s2 ) -> 
    { 
        out.printf( "Comparing %s to %s %n", s1, s2 ); 
        return s1.length() - s2.length(); 
    };


List<List<Integer>> ll = ...; 
ll.stream().reduce(new ArrayList(), (k, v) 
    -> { k.add( v.stream().reduce(0, (i1, i2) 
        -> Math.max(i1, i2)) ); return k; } );

Syntax


                
Lambda:         Argumentlist -> Body
                    
Argumentlist:  ( [ [type] name ] * )

Body:           Expression | Block

Argumentlisten

  • Argumentlisten er en liste over parametre "metoden" (lambda-uttrykket) tar, tilsvarende parameterlisten i en metode-deklarasjon.
  • Men 1: Man kan utelate typen til parametre.
  • Men 2: Hvis det er nøyaktig ett parameter uten type kan man utelate parantesene

Arrow

-> Nuff said

Body

Body er ett av følgende:

  • Et uttrykk (expression): 123, "hello", calcAmount()+32, etc.
  • En statement block uten returverdi (dvs type void):
    { myList.add(name); }
  • En statement block med returverdi:
    { sayHello(name); return name;}
  • EN metode-invokering uten klammeparanteser: out.println( "hello" )
  • En metodereferanse (mer om dette senere!)

OK, men...

Hvordan brukes dette? Hva skjer? Egentlig?

Functional interfaces 1

Tidligere kalt for "Single Abstract Method" / SAM interfaces.

Et interface eller en abstract klasse med nøyaktig EN ikke-implementert metode.

Kompilatoren vil automatisk oversette et lambdauttrykk til en "anonym metode" som implementerer metoden i et slikt interface.

En lambda er altså IKKE implementert vha anonyme klasser

(Demo: javap -c Anon\$1)

Functional interfaces 2

Metoder som ikke teller med i antallet ikke-implementerte metoder:

  • Metoder som faktisk er implementert i en abstrakt klasse (dooh)
  • Metoder som har en default implementasjon (mer om dette senere, kanskje)
  • Metoder som overrider metoder som finnes i Object-klassen (e.g. toString())

(Demo: Func1)

Functional abstract class?

Initielt skulle også abstrakte klasser kunne være "Functional" og implementeres vha lambda. Dette er droppet pga tekniske vanskeligheter samt muligheten for forvirrende kode:


public abstract class MyFunc {
    MyFunc() throws SQLException { sideEffectsHere(); }
    public abstract void doit(); 
}

// Somewhere far away from the declaration of MyFunc: 
MyFunc mf = () -> {}; // throws SQLException, and has sideeffects

"Effectively final"

Tidligere kunne anonyme klasser kun referere til variabler som var final. I java8 har man lempet på dette kravet: Man kan referere til variabler som er effectively final.

Uformell definisjon: En variabel er effectively final dersom det ikke ville blitt en kompileringsfeil dersom man deklarerte den som final.

Det samme gjelder lambdaer: Lambdaer som er closures kan (kun) referere til variabler som er effectively final

(Demo)

Metodereferanser

For å gjøre kode enda kortere og mer lettlest, har man introdusert metode-referanser. Dette er ikke det mange har savnet:


    Method m = MyBusinessClass::myMethod; 

Derimot kan dette brukes i lambdaer:


    myStringList.stream().map( String::length ); 

Både statiske metoder og instansemetoder kan refereres. Kompilatoren vil sjekke dette og gi deg

(Demo: Lambda.java)