Java 8, Lambda Expressions and You

Slides:



Advertisements
Similar presentations
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Winter 2013.
Advertisements

Java 8 Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author: Assaf Israel - Technion 2013.
CSE341: Programming Languages Lecture 11 Closures-ish Java & C Dan Grossman Fall 2011.
George Blank University Lecturer. CS 602 Java and the Web Object Oriented Software Development Using Java Chapter 4.
1 Generics and Using a Collection Generics / Parameterized Classes Using a Collection Customizing a Collection using Inheritance Inner Classes Use of Exceptions.
Language Evaluation Criteria
CSE 331 Software Design & Implementation Hal Perkins Autumn 2012 Java Classes, Interfaces, and Types 1.
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
Monads Technion – Institute of Technology Software Design (236700) Author: Gal Lalouche - Technion 2015 © 1.
Java 8 Lambda expressions and you Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author:
CISC6795: Spring Object-Oriented Programming: Polymorphism.
CSC 142 O 1 CSC 142 Java More About Inheritance & Interfaces [Reading: chapter 13]
Scala Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author: Gal Lalouche - Technion 2015.
Polymorphism, Inheritance Pt. 1 COMP 401, Fall 2014 Lecture 7 9/9/2014.
Effective Java: Generics Last Updated: Spring 2009.
1 Generics and Using a Collection Generics / Parameterized Classes Using a Collection Customizing a Collection using Inheritance Inner Classes Use of Exceptions.
Objects & Dynamic Dispatch CSE 413 Autumn Plan We’ve learned a great deal about functional and object-oriented programming Now,  Look at semantics.
CS162 Week 1 Kyle Dewey. Overview Basic Introduction CS Accounts Scala survival guide.
Application development with Java Lecture 21. Inheritance Subclasses Overriding Object class.
 In the java programming language, a keyword is one of 50 reserved words which have a predefined meaning in the language; because of this,
CSE 341 Section 10 Subtyping, Review, and The Future.
Spartan Programming Technion – Institute of Technology Author: Gal Lalouche - Technion 2016 © 1 “Minimalism isn't always the right choice, but it's.
Java 8 Lambda Expressions and You Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author:
Monads Technion – Institute of Technology Software Design (236700) Author: Gal Lalouche - Technion 2016 © 1.
Functional Processing of Collections (Advanced) 6.0.
Lecture 6:Interfaces and Abstract Classes Michael Hsu CSULA.
Catalog of Refactoring (6) Making Method Calls Simpler.
Lecture 5:Interfaces and Abstract Classes
Functional Programming
EECE 310: Software Engineering
Functional Programming and Stream API
Inheritance and Polymorphism
ADT’s, Collections/Generics and Iterators
Representation, Syntax, Paradigms, Types
Lambdas ---anonymous functions---
Closures and Streams cs784(Prasad) L11Clos
Generics, Lambdas, Reflections
Functional Processing of Collections (Advanced)
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Spring 2013.
Monads Author: Gal Lalouche - Technion 2017 © 1 1.
Functional Programming with Java
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Zach Tatlock Winter 2018.
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Spring 2017.
.NET and .NET Core 5.2 Type Operations Pan Wuming 2016.
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Winter 2013.
Java Programming Language
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Spring 2016.
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Autumn 2018.
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Autumn 2018.
CSE 1030: Data Structure Mark Shtern.
Generic programming in Java
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Autumn 2017.
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Autumn 2017.
Functional interface.
Representation, Syntax, Paradigms, Types
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Zach Tatlock Winter 2018.
Statement-Level Control Structures
A few of the more significant changes and additions. James Brucker
6.001 SICP Variations on a Scheme
Generics, Lambdas, Reflections
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Spring 2017.
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Spring 2016.
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Spring 2013.
CMPE212 – Reminders Assignment 2 due next Friday.
Review for Midterm 3.
„Lambda expressions, Optional”
Chengyu Sun California State University, Los Angeles
CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping Dan Grossman Spring 2019.
Java’s Stream API.
CSE341: Programming Languages Lecture 16 Datatype-Style Programming With Lists or Structs Dan Grossman Spring 2019.
Presentation transcript:

Java 8, Lambda Expressions and You Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author: Gal Lalouche - Technion 2017 ©

Life before Java 8 Extracting employee names Extracting employee ages public List<String> empNames(List<Employee> employees) { List<String> $ = new ArrayList<>(); for (Employee emp : employees) $.add(emp.getName()); return $; } Variation Duplication Technically, the generic arguments (String vs. Integer are also a variation; but we’ll get that for free via type inference). public List<Integer> empAges(List<Employee> employees) { List<Integer> $= new ArrayList<>(); for (Employee emp : employees) $.add(emp.getAge()); return $; } Author: Gal Lalouche - Technion 2017©

Life before Java 8 (cont.) Lets identify the control structure, and extract the behavior into an object public List<String> empNames(List<Employee> employees) { List<String> $ = new ArrayList<>(); for (Employee emp : employees) $.add(emp.getName()); return $; } public interface Mapper<U, T> { public T map(U u); } public <U, T> List<T> map( List<U> list, Mapper<? super U, ? extends T> m) { List<T> $ = new ArrayList<>(); for (U u : list) $.add(m.map(u)); return $; } Note the use (and need) of both co- and contra-variance wildcards Author: Gal Lalouche - Technion 2017©

Life before Java 8 (cont.) Extracting employee names Extracting employee ages List<String> empNames = map(employees, new Mapper<Employee,String>() { public String map(Employee e) { return e.getName(); } }); Redundant List<Integer> empAges = map(employees, new Mapper<Employee,Integer>() { public Integer map(Employee e) { return e.getAge(); } }); One could argue that even declaring a List<String> is redundant (compare with var in C#) Author: Gal Lalouche - Technion 2017©

In the Kingdom of Nouns We removed the code duplication, but this is still very verbose… Semantically, map is a higher level function This means that it accepts a function as an argument (or returns a function) Syntactically, functions do not exist as first class entities All verbs (functions) have be accompanied by a noun (class) http://steve-yegge.blogspot.co.il/2006/03/execution-in-kingdom-of-nouns.html Prior to Java 8, Java was the only programming language in popular use without anonymous functions / blocks / lambdas / function pointers This is not purely a syntactic issue; Java also lacked proper support for such function in its collections and standard libraries Some libraries, like Guava, attempted to fill the void Author: Gal Lalouche - Technion 2017©

Enter Java 8 ! Extracting employee names: Extracting employee ages: Still very verbose compared to other languages (C#, Scala, Python) “boiler-plate” ratio lessens when we compose actions (see later) List<String> empNames = employees.stream() .map(x -> x.getName()) .collect(Collectors.toList()); List<Integer> empAge = employees.stream() .map(Employee::getAge) // method reference instead of lambda .collect(Collectors.toList()); Author: Gal Lalouche - Technion 2017©

Let’s take a deeper look… List<String> empNames = employees.stream() .map(x -> x.getName()) .collect(Collectors.toList()); stream() is a default method of List map is a higher level function of Stream x -> x.getName() is a lambda expression collect turns the Stream back to a normal Collection (in our case, a List) Let’s go over each of these terms one by one Author: Gal Lalouche - Technion 2017©

default Methods List<String> empNames = employees.stream() .map(x -> x.getName()) .collect(Collectors.toList()); default methods are (default) implementations for interfaces Can be overridden extending interfaces and implementing classes Adds new functionality to an existing interface without breaking all client code In our case, we added the stream() method to Collection interface Foo { void a(); // regular abstract method default void b() { // can also be overriden System.out.println("I'm a default method!"); } No stream() method on iterable because reasons: http://stackoverflow.com/questions/23114015/why-does-iterablet-not-provide-stream-and-parallelstream-methods. Use the ridiculously verbose StreamSupport.stream(iter.spliterator(), false); Author: Gal Lalouche - Technion 2017©

Comparison to other languages / features So is this the same as multiple inheritance? Nope; more similar to Traits There is neither conflict resolution nor constructors, so the model is much simpler So are these extension methods (a la C#)? No, because extension methods are actually syntactic sugar for static decorators You can’t add methods to library classes (e.g., in C# you can add extension methods to String). Solutions in other languages Ruby – mixins Python/Javascript – monkey patching Scala – implicits / pimp my library Haskell – type classes No stream() method on iterable because reasons: http://stackoverflow.com/questions/23114015/why-does-iterablet-not-provide-stream-and-parallelstream-methods. Use the ridiculously verbose StreamSupport.stream(iter.spliterator(), false); Author: Gal Lalouche - Technion 2017©

Higher order functions List<String> empNames = employees.stream() .map(x -> x.getName()) .collect(Collectors.toList()); map is a higher order function in stream A function that takes a function Other higher order functions in Stream filter, map, flatMap, sorted, reduce, … Similar libraries in other languages LINQ in C#, itertools in Python, Enumerable in Ruby, etc. Author: Gal Lalouche - Technion 2017©

Streams Stream is the gateway to the "functional collections" in Java 8 Provide a uniform API (why is this important?) We only iterate over a stream once, even if we have two or more higher level functions This is because streams are lazily evaluated Until we collect (or form some other reduction), no iteration takes place collect is a form of mutable reduction i.e., it reduces to a mutable container Other reductions include forEach and, well, reduce Streams also give us “free” parallelization (why is it so easy?) Streams, or lazy lists, were taught in Programming Languages 234319 as part of ML In Haskell, which is lazily evaluated, (lazy) lists are the most basic data structure The “free” parallelization is not a substitute to knowing how to write truly concurrent code! It only works well in very specific cases List<String> empNames = employees.stream() .parallel() .map(x -> x.getName()) .collect(Collectors.toList()); Author: Gal Lalouche - Technion 2017©

Streams: Caveats Streams are “single serving” only! This code will throw an exception: This too: Avoid returning Stream from a public function, or keeping one as a field, An Iterable or Collection is usually more suitable Although there are some (rare) cases where it’s appropriate, there are usually better (monadic) types Stream<Student> stream = students.stream(); Stream<String> names = stream.map(Student::getName); Stream<Integer> ages = stream.map(Student::getAge); Stream<String> names = students.stream.map(Student::getName); stream.forEach(this::printStudent); stream.forEach(this::addStudentToDatabase); Author: Gal Lalouche - Technion 2017©

Lambdas and SAMs List<String> empNames = employees.stream() The signature for map is: map(Function<? super T,? extends R> mapper) And here is the signature for Function (default methods retracted): An interface which has single abstract (i.e., non-default) method (often abbreviated SAM) can be called a functional interface Lambdas are just syntactic sugar for implementing functional interfaces Method reference (::) and lambdas are interchangeable, where applicable References are considered “more elegant” (as we will see later) So is Java a functional language now? Functions aren’t first-class citizens; functions aren’t even a proper part of the Java language, just a standard library interface Although an alternative interpretation could argue that interfaces are the new functions List<String> empNames = employees.stream() .map(x -> x.getName()) .collect(Collectors.toList()); interface Function<T, R> { R apply(T t); } The lambda expression just implements the abstract method of the interface The compiler does a bit of magic for us to create an instance of a Function object Note, that this does not work for abstract classes (why?) Author: Gal Lalouche - Technion 2017©

Lambdas (cont.) This design choice has a great pro: we can also use lambda with legacy API! Old code New code We can use the convenience @FunctionalInterface annotation to tell the compiler that the interface should be functional (a la @Override) new Thread(new Runnable() { @Override public void run() { System.out.println("Kill me :["); } }).start(); new Thread(() -> System.out.println("PARTEH! :D|-< :D/-< :D\-<)).start(); @FunctionalInterface interface Foo { void bar(); void bazz(); } // won’t compile Author: Gal Lalouche - Technion 2017©

More API examples Assure we are not hiring anyone underage What’s this? Assure we are not hiring anyone underage Find the highest paid individual in the company What is returned if the list is empty? Instead of working with null, a new type Optional<T> is returned Optional<T> can be present (i.e. not null) or empty (i.e. null) Has a method get() that returns T or throws an exception assert employees.stream().noneMatch(x -> x.age < 18); Optional<Employee> $ = employees.stream().maxBy((x, y) -> x.salary – y.salary); Stream is also a Monad Author: Gal Lalouche - Technion 2017©

Wait, what’s wrong with nulls? The billion dollar mistake nulls are incredibly dangerous! Often unchecked until used a “sleeper agent” that destroys the application, its origin is hard to trace By returning an Optional, we are explicit in our result type Types are better than comments! Optional also has higher order functions filter will return empty if the predicate returns false Optional<Employee> richest = … Optional<Integer> ageOfRichest = richest.map(Employee::getAge); Stream is also a Monad richestEmployee.filter(x -> x.age >= 18); Author: Gal Lalouche - Technion 2017©

Composing Optionals Optionals compose using flatMap We will generalize this pattern when we learn about monads later in the course // working with nulls Student s = getStudent(); if (s == null) return null; Course c = s.getCourse("Software Design"); if (c == null) Exam e = c.getMoedA(); if (e == null) return e.getGrade(); // but if we returned Optionals… getStudent() .flatMap(Student::getCourse) .flatMap(Course::getMoedA) .flatMap(Exam::getGrade) Stream is also a Monad Author: Gal Lalouche - Technion 2017©

Sorts in place! Why is this bad? A more complex example Get Israeli students with a top grade sorted by name in Java 7 In Java 8: Sorts in place! Why is this bad? List<Student> topGrades = new ArrayList<>(); Collections.sort(students, new Comparator<Student>() { public int compare(Student student1, Student student2) { return student1.getName().compareTo(student2.getName()); } }); for (Student student: students) if (student.getCountry() == "Israel") if (student.getGrade() >= 90) topGrades.add(student); Depth of 3! Java 7: We can sort after filtering for performance, but who cares  This also saves the need of sorting the original students list, but the original API is still broken for its mutability Java 8: We only iterate over the collection once List<Students> topStudents = students.stream() .filter(x -> x.getCountry() == "Israel") .filter(x -> x.getGrade() >= 90) .sorted(Comparator.comparing(Student::getName)) .collect(Collectors.toList()); Author: Gal Lalouche - Technion 2017©

Other cool tricks Sum of all salaries in the company with "map-reduce" Count the number of employees by rank Streams compose using flatMap too! (they're also monads) employees.stream() .mapToInt(Employee::getSalary)// note the mapToInt... why? .reduce(0, Integer::sum) // could also be done with Lambdas, or simply .sum() Map<Rank, Long> countByRank = employees.stream().collectors( Collectors.groupingBy(Employee::getRank, Collectors.counting()); List<Student> allIsraeliStudents = universities.stream() .flatMap(u -> u.getFaculties().stream()) .flatMap(f -> f.getStudents().stream()) .collect(Collectors.toList()); Author: Gal Lalouche - Technion 2017©

Declarative versus Imperative programming Streams and Optionals are an example of moving from imperative code to declarative code In imperative code we write the exact, low level steps: Create a new list object Iterate over the original list For every entry, apply some function f on it Add the result of f in the new list Return the new list In declarative programming, we write a higher level description: map all elements in the list using some function f collect to a List Author: Gal Lalouche - Technion 2017©

Declarative versus Imperative (Cont.) Declarative code is shorter, more precise and explicit, more readable, and less error-prone You can do pretty anything inside a for loop That means you have to read the entire body to know what’s going on More room for bugs Declarative code is written in a higher level of abstraction In our case, maps and filters, rather than object creation and modification Higher order functions instead of control structures and primitive checks Less moving parts, hide the unnecessary details

Dec. v Imp. – A spectrum, not dichotomy Before Java 5, we had to iterate by index, or use the iterator directly Even more bugs: infinite loop, index modifications Using list.add is more declarative than managing the internal data structure on your own Using a library/function is usually more declarative than inlining its code Applies to syntax, not just semantics An array initializer (new int[] {1, 2, 3}) is more declarative than doing it manually A lambda expression is more declarative than an anonymous functions, but a method reference is more declarative than a lambda expression Rule of thumb: Less tokens ⇒ More declarative

TL;DR Avoid loops, use Streams Avoid nulls, use Optionals Almost any loop can be replaced with a Stream call The new version of IntelliJ does this automagically Avoid nulls, use Optionals Optionals are clearer, safer, compose better, and support higher level functions Only use nulls when dealing with legacy APIs Prefer declarative to imperative code whenever possible The ≠ sign is provided by font ligatures (https://blog.jetbrains.com/idea/2016/06/intellij-idea-2016-2-eap-case-only-renames-in-git-ligatures-background-images-and-more/)

Appendix What else is new in Java 8? New Date and Time APIs Everything is now immutable, and immutable is good Support for unsigned arithmetic (no uint type) Embedding JavaScript code in Java Better integration with JavaFX Java library for developing rich client applications Alternative to swing, which is no longer in active development ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("console.log('Hello World!');"); Date API is pretty much a copy of Joda The implementation of unsigned support is borderline trolling Author: Gal Lalouche - Technion 2017©