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

Simon Ritter - Lambda Expressions in JDK8 Going Beyond The Basics

Simon Ritter - Lambda Expressions in JDK8 Going Beyond The Basics

Riga Dev Day

January 30, 2015
Tweet

More Decks by Riga Dev Day

Other Decks in Programming

Transcript

  1. Lambda  Expressions  In  JDK8   Going  Beyond  The  Basics  

    Simon  Ri7er   Head  of  Java  Technology  Evangelism   Oracle  Corp.     Twi7er:  @speakjava   Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.      
  2. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Safe  Harbor  Statement   The  following  is  intended  to  outline  our  general  product  direcTon.  It  is  intended  for   informaTon  purposes  only,  and  may  not  be  incorporated  into  any  contract.  It  is  not  a   commitment  to  deliver  any  material,  code,  or  funcTonality,  and  should  not  be  relied  upon   in  making  purchasing  decisions.  The  development,  release,  and  Tming  of  any  features  or   funcTonality  described  for  Oracle’s  products  remains  at  the  sole  discreTon  of  Oracle.   2  
  3. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Program  Agenda   Lambdas  And  Streams  Primer   Delaying  ExecuTon   Avoiding  loops  in  Streams   The  Art  of  ReducTon   Conclusions   1   2   3   4   5   Oracle  ConfidenTal  –  Internal/Restricted/Highly  Restricted   3  
  4. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Lambda  Expressions  In  JDK8   •  Old  style,  anonymous  inner  classes   •  New  style,  using  a  Lambda  expression     5   Simplified  Parameterised  Behaviour   new  Thread(new  Runnable  {      public  void  run()  {          doSomeStuff();      }   }).start();   new  Thread(()  -­‐>  doSomeStuff()).start();  
  5. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Lambda  Expressions   •  Lambda  expressions  represent  anonymous  funcTons   – Same  structure  as  a  method   •  typed  argument  list,  return  type,  set  of  thrown  excepTons,  and  a  body   – Not  associated  with  a  class   •  We  now  have  parameterised  behaviour,  not  just  values   Some  Details   double  highestScore  =  students.                      filter(Student  s  -­‐>  s.getGradYear()  ==  2011).                      map(Student  s  -­‐>  s.getScore())                      max();   What   How  
  6. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Lambda  Expression  Types   •  Single-­‐method  interfaces  are  used  extensively  in  Java   – DefiniTon:  a  func%onal  interface  is  an  interface  with  one  abstract  method   – Func%onal  interfaces  are  idenTfied  structurally   – The  type  of  a  lambda  expression  will  be  a  func%onal  interface   •  Lambda  expressions  provide  implementaTons  of  the  abstract  method   interface  Comparator<T>    {  boolean  compare(T  x,  T  y);  }    interface  FileFilter          {  boolean  accept(File  x);  }    interface  Runnable              {  void  run();  }    interface  ActionListener  {  void  actionPerformed(…);  }    interface  Callable<T>        {  T  call();  }  
  7. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Local  Variable  Capture   •  Lambda  expressions  can  refer  to  effec%vely  final  local  variables  from  the   surrounding  scope   – EffecTvely  final:  A  variable  that  meets  the  requirements  for  final  variables  (i.e.,   assigned  once),  even  if  not  explicitly  declared  final   – Closures  on  values,  not  variables   void  expire(File  root,  long  before)  {        root.listFiles(File  p  -­‐>  p.lastModified()  <=  before);   }  
  8. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          What  Does  ‘this’  Mean  In  A  Lambda   •  ‘this’  refers  to  the  enclosing  object,  not  the  lambda  itself   •  Think  of  ‘this’  as  a  final  predefined  local   •  Remember  the  Lambda  is  an  anonymous  func%on   – It  is  not  associated  with  a  class   – Therefore  there  can  be  no  ‘this’  for  the  Lambda  
  9. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Referencing  Instance  Variables   Which  are  not  final,  or  effecTvely  final     class  DataProcessor  {      private  int  currentValue;        public  void  process()  {          DataSet  myData  =  myFactory.getDataSet();          dataSet.forEach(d  -­‐>  d.use(currentValue++));      }   }  
  10. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Referencing  Instance  Variables   The  compiler  helps  us  out     class  DataProcessor  {      private  int  currentValue;        public  void  process()  {          DataSet  myData  =  myFactory.getDataSet();          dataSet.forEach(d  -­‐>  d.use(this.currentValue++);      }   }   ‘this’  (which  is  effecTvely  final)   inserted  by  the  compiler  
  11. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Type  Inference   •  The  compiler  can  oeen  infer  parameter  types  in  a  lambda  expression   § Inferrence  based  on  the  target  funcTonal  interface’s  method  signature   •  Fully  staTcally  typed  (no  dynamic  typing  sneaking  in)   – More  typing  with  less  typing   List<String>  list  =  getList();   Collections.sort(list,  (String  x,  String  y)  -­‐>  x.length()  -­‐  y.length());   Collections.sort(list,  (x,  y)  -­‐>  x.length()  -­‐  y.length());   static  T  void  sort(List<T>  l,  Comparator<?  super  T>  c);  
  12. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          FuncTonal  Interface  DefiniTon   •  An  interface   •  Must  have  only  one  abstract  method   – In  JDK  7  this  would  mean  only  one  method  (like  ActionListener)   •  JDK  8  introduced  default  methods   – Adding  mulTple  inheritance  of  types  to  Java   – These  are,  by  definiTon,  not  abstract  (they  have  an  implementaTon)   •  JDK  8  also  now  allows  interfaces  to  have  staTc  methods   – Again,  not  abstract   •  @FunctionalInterface  can  be  used  to  have  the  compiler  check   13  
  13. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Is  This  A  FuncTonal  Interface?   14   @FunctionalInterface   public  interface  Runnable  {      public  abstract  void  run();   }   Yes.  There  is  only   one  abstract  method  
  14. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Is  This  A  FuncTonal  Interface?   15   @FunctionalInterface   public  interface  Predicate<T>  {      default  Predicate<T>  and(Predicate<?  super  T>  p)  {…};      default  Predicate<T>  negate()  {…};      default  Predicate<T>  or(Predicate<?  super  T>  p)  {…};      static  <T>  Predicate<T>  isEqual(Object  target)  {…};      boolean  test(T  t);   }   Yes.  There  is  sTll  only   one  abstract  method  
  15. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Is  This  A  FuncTonal  Interface?   16   @FunctionalInterface   public  interface  Comparator  {      //  Static  and  default  methods  elided      int  compare(T  o1,  T  o2);      boolean  equals(Object  obj);   }   The  equals(Object)   method  is  implicit  from   the  Object  class     Therefore  only  one   abstract  method  
  16. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Stream  Overview   •  A  stream  pipeline  consists  of  three  types  of  things   – A  source   – Zero  or  more  intermediate  operaTons   – A  terminal  operaTon   •  Producing  a  result  or  a  side-­‐effect   Pipeline   int  total  =  transactions.stream()      .filter(t  -­‐>  t.getBuyer().getCity().equals(“London”))      .mapToInt(Transaction::getPrice)      .sum();   Source   Intermediate  operaTon   Terminal  operaTon  
  17. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Stream  Sources   •  From  collecTons  and  arrays   – Collection.stream()   – Collection.parallelStream()   – Arrays.stream(T  array)  or  Stream.of()   •  StaTc  factories   – IntStream.range()   – Files.walk()   •  Roll  your  own   – java.util.Spliterator   Many  Ways  To  Create  
  18. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Stream  Terminal  OperaTons   •  The  pipeline  is  only  evaluated  when  the  terminal  operaTon  is  called   – All  operaTons  can  execute  sequenTally  or  in  parallel   – Intermediate  operaTons  can  be  merged   •  Avoiding  mulTple  redundant  passes  on  data   •  Short-­‐circuit  operaTons  (e.g.  findFirst)   •  Lazy  evaluaTon   – Stream  characterisTcs  help  idenTfy  opTmisaTons   •  DISTINT  stream  passed  to  distinct()  is  a  no-­‐op  
  19. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          OpTonal  Class   •  Terminal  operaTons  like  min(),  max(),  etc  do  not  return  a  direct  result   •  Suppose  the  input  Stream  is  empty?   •  Optional<T>   – Container  for  an  object  reference  (null,  or  real  object)   – Think  of  it  like  a  Stream  of  0  or  1  elements   – use  get(),  ifPresent()  and  orElse()  to  access  the  stored  reference   – Can  use  in  more  complex  ways:  filter(),  map(),  etc   •  gpsMaybe.filter(r  -­‐>  r.lastReading()  <  2).ifPresent(GPSData::display);   Helping  To  Eliminate  the  NullPointerException  
  20. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Lambda  Expressions  and   Delayed  ExecuTon    
  21. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Performance  Impact  For  Logging   •  Heisenberg’s  uncertainty  principle   •  Semng  log  level  to  INFO  sTll  has  a  performance  impact   •  Since  Logger  determines  whether  to  log  the  message  the  parameter  must   be  evaluated  even  when  not  used   22   logger.finest(getSomeStatusData());   Always  executed  
  22. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Supplier<T>   •  Represents  a  supplier  of  results   •  All  relevant  logging  methods  now  have  a  version  that  takes  a  Supplier   •  Pass  a  descripTon  of  how  to  create  the  log  message   – Not  the  message   •  If  the  Logger  doesn’t  need  the  value  it  doesn’t  invoke  the  Lambda   •  Can  be  used  for  other  condiTonal  acTviTes   23   logger.finest(()  -­‐>  getSomeStatusData());  
  23. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          FuncTonal  v.  ImperaTve   •  For  funcTonal  programming  you  should  not  modify  state   •  Java  supports  closures  over  values,  not  closures  over  variables   •  But  state  is  really  useful…   25  
  24. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          CounTng  Methods  That  Return  Streams   26   S?ll  Thinking  Impaera?vely  Way   Set<String>  sourceKeySet  =  streamReturningMethodMap.keySet();     LongAdder  sourceCount  =  new  LongAdder();     sourceKeySet.stream()      .forEach(c  -­‐>              sourceCount.add(streamReturningMethodMap.get(c).size()));  
  25. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          CounTng  Methods  That  Return  Streams   27   Func?onal  Way   sourceKeySet.stream()      .mapToInt(c  -­‐>  streamReturningMethodMap.get(c).size())      .sum();  
  26. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          PrinTng  And  CounTng  FuncTonal  Interfaces   28   S?ll  Thinking  Impera?vely  Way   LongAdder  newMethodCount  =  new  LongAdder();     functionalParameterMethodMap.get(c).stream()      .forEach(m  -­‐>  {          output.println(m);            if  (isNewMethod(c,  m))                newMethodCount.increment();      });        return  newMethodCount.intValue();  
  27. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          PrinTng  And  CounTng  FuncTonal  Interfaces   29   More  Func?onal,  But  Not  Pure  Func?onal   int  count  =  functionalParameterMethodMap.get(c).stream()      .mapToInt(m  -­‐>  {          int  newMethod  =  0;          output.println(m);            if  (isNewMethod(c,  m))                newMethod  =  1;            return  newMethod      })      .sum();   There  is  sTll  state  being   modified  in  the  Lambda  
  28. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          PrinTng  And  CounTng  FuncTonal  Interfaces   30   Even  More  Func?onal,  But  S?ll  Not  Pure  Func?onal   int  count  =  functionalParameterMethodMap.get(nameOfClass)      .stream()      .peek(method  -­‐>  output.println(method))      .mapToInt(m  -­‐>  isNewMethod(nameOfClass,  m)  ?  1  :  0)        .sum();   Strictly  speaking  prinTng   is  a  side  effect,  which  is   not  purely  funcTonal  
  29. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          The  Art  Of  ReducTon   (Or  The  Need  to  Think  Differently)    
  30. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          A  Simple  Problem   •  Find  the  length  of  the  longest  line  in  a  file   •  Hint:  BufferedReader  has  a  new  method,  lines(),  that  returns  a  Stream   32   BufferedReader  reader  =  ...     reader.lines()      .mapToInt(String::length)      .max()      .getAsInt();  
  31. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Another  Simple  Problem   •  Find  the  length  of  the  longest  line  in  a  file   33  
  32. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Naïve  Stream  SoluTon   •  That  works,  so  job  done,  right?   •  Not  really.  Big  files  will  take  a  long  Tme  and  a  lot  of  resources   •  Must  be  a  be7er  approach   34   String  longest  =  reader.lines().      sort((x,  y)  -­‐>  y.length()  -­‐  x.length()).      findFirst().      get();  
  33. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          External  IteraTon  SoluTon     •  Simple,  but  inherently  serial   •  Not  thread  safe  due  to  mutable  state     35   String  longest  =  "";     while  ((String  s  =  reader.readLine())  !=  null)      if  (s.length()  >  longest.length())          longest  =  s;  
  34. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Recursive  Approach:  The  Method   36   String  findLongestString(String  s,  int  index,  List<String>  l)  {      if  (index  ==  l.size()  -­‐  1)  {          if  (s.length()  >  l.get(index).length())              return  s;          return  l.get(index);      }              String  s2  =  findLongestString(l.get(start),  index  +  1,  l);              if  (s.length()  >  s2.length())          return  s;      return  s2;   }  
  35. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Recursive  Approach:  Solving  The  Problem   •  No  explicit  loop,  no  mutable  state,  we’re  all  good  now,  right?   •  Unfortunately  not  -­‐  larger  data  sets  will  generate  an  OOM  excepTon   37   List<String>  lines  =  new  ArrayList<>();     while  ((String  s  =  reader.readLine())  !=  null)              lines.add(s);     String  longest  =  findLongestString("",  0,  lines);  
  36. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          A  Be7er  Stream  SoluTon   •  The  Stream  API  uses  the  well  known  filter-­‐map-­‐reduce  pa7ern   •  For  this  problem  we  do  not  need  to  filter  or  map,  just  reduce                    Optional<T>  reduce(BinaryOperator<T>  accumulator)     •  BinaryOperator  is  a  subclass  of  BiFunction,  but  all  types  are  the  same   •  R  apply(T  t,  U  u)    or  T  apply(T  x,  T  y)       38  
  37. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          A  Be7er  Stream  SoluTon   •  The  key  is  to  find  the  right  accumulator   – The  accumulator  takes  a  parTal  result  and  the  next  element,  and  returns  a  new   parTal  result   – In  essence  it  does  the  same  as  our  recursive  soluTon   – But  back  to  front   – And  without  all  the  stack  frames  or  List  overhead     39  
  38. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          A  Be7er  Stream  SoluTon   •  Use  the  recursive  approach  as  an  accululator  for  a  reducTon   40   String  longestLine  =  reader.lines()      .reduce((x,  y)  -­‐>  {          if  (x.length()  >  y.length())              return  x;          return  y;      })      .get();  
  39. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          A  Be7er  Stream  SoluTon   •  Use  the  recursive  approach  as  an  accululator  for  a  reducTon   41   String  longestLine  =  reader.lines()      .reduce((x,  y)  -­‐>  {          if  (x.length()  >  y.length())              return  x;          return  y;      })      .get();   x  in  effect  maintains  state  for   us,  by  providing  the  parTal   result,  which  is  the  longest   string  found  so  far  
  40. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          The  Simplest  Stream  SoluTon   •  Use  a  specialised  form  of  max()   •  One  that  takes  a  Comparator  as  a  parameter   •  comparingInt()  is  a  staTc  method  on  Comparator   – Comparator<T>  comparingInt(ToIntFunction<?  extends  T>  keyExtractor)   42   reader.lines()      .max(comparingInt(String::length))      .get();  
  41. Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.

          Conclusions   •  Lambdas  provide  a  simple  way  to  parameterise  behaviour   •  The  Stream  API  provides  a  funcTonal  style  of  programming   •  Very  powerful  combinaTon   •  Does  require  developers  to  think  differently   •  Avoid  loops,  even  non-­‐obvious  ones!   44  
  42. Simon  Ri7er   Oracle  CorporarTon     Twi7er:  @speakjava  

    Copyright  ©  2014,  Oracle  and/or  its  affiliates.  All  rights  reserved.