A brief tour of modern Java
by Sina Madani
A brief history…
• February 1997 – Java 1.1
AWT, inner classes, JDBC, reflection (introspection only), RMI, JavaBeans…
• December 1998 – Java 2
• JIT compiler, Collections framework, JIT compiler, CORBA IDL…
• May 2000 – Java 3
• Hotspot, JNDI, JDPA…
• February 2002 – Java 4
• assert keyword, NIO, exception chaining, IPv6, prefs, APIs for security,
logging, image…
A brief history…
• September 2004 – Java 5 (Java as we know it!)
• Generics, annotations, enum, varargs, for-each, static imports, Scanner,
auto-boxing/unboxing, concurrent utilities (task scheduling, atomics, locks…)
• December 2006 – Java 6 (one of the most popular versions)
• General back-end improvements, nothing new that’s noteworthy
• July 2011 – Java 7 (Project Coin)
• invokedynamic, Strings in switch, type inference (“diamonds”), binary
literals, try-with-resources, fork/join framework, multi-catch, new file I/O…
• March 2014 – Java 8
• July 2017 (we hope!) – Java 9
Why is Java 8 so significant?
• Like Java 5, Java 8 makes fundamental additions to the language
• It (should!) change how programmers think and code
• It embraces functional programming
• It brings Java to the modern era
• Java is “plagued” by its origins
• Lots of “modern” JVM languages are far superior: Groovy, Scala, Kotlin…
• Java 8 makes significant strides to keep up to date
default and static methods in interfaces
• Interface methods can contain a default implementation
• Implementing classes do not need to override default methods
• “Poor man’s multiple inheritance”?
• Partially removes the need for X as interface and AbstractX
• A convenient way to make it less laborious to implement interfaces
• Allows interfaces to evolve without breaking compatibility
• Static methods also allow for evolution – no longer need to have a
wrapper class for implementing utility methods
• Partially motivated by other new features in Java 8
Diamond problem
interface I1 {
default void foo() {
System.out.println(“I1::foo”);
}
}
interface I2 {
default void foo() {
System.out.println(“I2::foo”);
}
}
class BC1 {
public void foo() {
System.out.println(“BC1::foo”);
}
}
class Derived extends BC1 implements I1, I2 {}
• Calling new Derived().foo() results in “BC1::foo”
• What if you have:
class Derived implements I1, I2 {}
new Derived().foo();
• This won’t compile – which foo() do we mean?
• Need to implement it to resolve this issue
• Can still call the base implementations if we want
class Derived implements I1, I2 {
public void foo() {
System.out.println(“Derived::foo”);
I1.super.foo();
I2.super.foo();
}
}
Functional interfaces
• A functional interface has exactly one non-default method
• Runnable is a functional interface – just one method void run()
• An interface with one (unimplemented) method is really a function
• Think about it: when you pass a Runnable to a Thread, you are really telling
the thread to execute the body of the run() method, and nothing else
• Due to Java’s principle of “everything has to be an object”, functions haven’t
been treated with the dignity they deserve.
• Functional interfaces promote functions to “first-class” citizens
• “Hey daddy, can I go alone?” “No, you need to be wrapped in a class.”
• This is no longer the case with Java 8
Lambda expressions
• This is the “headline” feature of Java 8 (deservedly so!)
• A replacement for anonymous functional interface implementations
• You no longer need the “ceremony” for doing this common task
• A lambda expression allows you to define the body of a functional
interface’s method without all the boilerplate around it
Before Java 8
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.print(currentThread().getName());
}
});
With lambdas
Thread t = new Thread (() -> {
System.out.print(currentThread().getName());
});
Method references
• Even more concise than lambdas
• Suppose all you want to do is pass the parameters to another method
• E.g. a Runnable in ThreadFactory
• With lambdas, this is as follows:
ThreadFactory tf = new ThreadFactory(r -> {
return new Thread(r);
});
ThreadFactory before Java 8
ThreadFactory tf = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
};
ThreadFactory with method reference
ThreadFactory tf = Thread::new;
Things to know with lambdas
• Lambdas are not simply syntactic sugar for anonymous inner classes
• No .class files are generated – dynamic dispatch used instead
• Variables of the outer class accessed within the body of lambda
expression should be (effectively) final
• That is, you can’t change the value from within the lambda expression
• Their value must be assigned – need to be statically determinable
• You can’t throw checked exceptions from within a lambda
• Exceptions need to be handled within the lambda
• There are ways to “cheat” this using wrappers
java.util.function
• This is what drives lambda expressions and method references
• Function<T,R> R apply(T t);
• Consumer<T> void accept(T t);
• Supplier<T> T get();
• Predicate<T> boolean test(T t);
• UnaryOperator<T> T apply(T t);
• Also has function types for every combination of int, long, double
• E.g. IntConsumer, IntToLongFunction, DoubleToIntConsumer etc.
• Every interface also has a “Bi” version – accepts two parameters
• e.g. BiFunction<T,U,R> R apply(T t, U u);
Example: Collections.forEach
• Collections API (java.util.Collection and friends) has been updated to
take advantage of the functional interfaces
• One of the most common tasks is to iterate over a collection
Arrays.asList(5, 7, 9, 16).forEach(System.out::println);
• forEach expects a Consumer<T> where T is the type of the collection
Streams API (java.util.stream)
• A Stream is an abstract pipeline of computation
• Allows for performing aggregate operations on data sources
• A data source could be a collection, arrays, iterators, I/O etc.
• java.util.stream only consists of interfaces, not implementation
• Includes Stream<T>, IntStream, DoubleStream, LongStream etc.
• Common operations include:
• map, reduce, filter, findAny, findFirst, forEach, count, distinct, iterate,
generate, limit, min, max, sorted, skip, peek, of, noneMatch, allMatch,
anyMatch, collect, toArray
Properties of Streams
• Lazily evaluated
• Most (“intermediate”) operations just build up the pipeline of computation
• Only “terminal operations” will trigger any actual “work”
• Terminal operations include forEach, collect, findAny, sum, reduce etc.
• Streams do not store any data; nor do they modify the source
• Always return a new Stream for intermediate operations
• Streams are consumable – traverse the data source only once
• Single-pass pipeline and laziness make streams very efficient
• Can be unbounded (infinite streams)
• A convenient way to express data-parallel computations
Language evolution with example
• Suppose you have a set of names
• Create a capitalised subset of the names shorter than 5 characters
E.g. [“Sarah”, “Beth”, “Hamid”, “Marcus”, “Chris”, “Jim”]
becomes
[“BETH”, “CHRIS”, “JIM”]
Java 1
Set getShortNames(Set names) {
Set shortNames = new HashSet();
Iterator iter = names.iterator();
while (iter.hasNext()) {
String name = (String) iter.next();
if (name.length() < 5)
shortNames.add(name.toUpperCase());
}
return shortNames;
}
Java 5
Set<String> getShortNames(Set<String> names) {
Set<String> shortNames = new HashSet<String>();
for (String name : names) {
if (name.length() < 5)
shortNames.add(name.toUpperCase());
}
return shortNames;
}
Java 8
Set<String> getShortNames(Set<String> names) {
return names.stream()
.filter(name -> name.length() < 5)
.map(String::toUpperCase)
.collect(Collectors.toSet());
}
Extension 1
• Get the resulting set of names as a single string separated by commas
• For example:
Set [“Chris”, “Charlie”, “Megan”, “Amy”, “Ben”, “Zoe”]
becomes:
String “CHRIS, AMY, BEN, ZOE”
Imperative style (Java 7)
String getShortNames(Set<String> names) {
StringBuilder shortNames = new StringBuilder();
Iterator iter = names.iterator();
for (int i = 0; i < iter.hasNext(); i++) {
String name = iter.next();
if (name.length() < 5)
shortNames.append(name.toUpperCase());
if (i != names.size())
shortNames.append(“, ”);
}
return shortNames.toString();
}
Declarative style (Java 8)
String getShortNames(Set<String> names) {
return names.stream()
.filter(name -> name.length() < 5)
.map(String::toUpperCase)
.collect(Collectors.joining(“, ”));
}
Extension 2
• The set of names has > 1 million elements
• We need to process them faster
• Create a multi-threaded version to speed things up
Java 1
• “I‘m handing in my resignation.”
Java 5
• “Sorry boss, I’m not feeling well today.”
Java 8
Set<String> getShortNames(Set<String> names) {
return names.parallelStream()
.filter(name -> name.length() < 5)
.map(String::toUpperCase)
.collect(Collectors.toSet());
}
Efficiency example
“Find the square root of the first even number greater than k.”
Suppose:
List<Integer> numbers = asList(1, 2, 5, 3, 7, 8, 12, 10);
and
k = 4
Imperative style
double result = 0d;
for (number : numbers) {
if (number > k && number % 2 == 0) {
result = Math.sqrt(number);
break;
}
}
return result;
Stream (functional style)
return numbers.stream()
.filter(number -> number > k)
.filter(number -> number % 2 == 0)
.map(Math::sqrt)
.findFirst();
Which does more work?
• Imperative style:
• 1 < 4, 2 < 4, 5 > 4, 5 % 2 > 0, 3 < 4, 7 > 4, 7 % 2 > 0, 8 > 4, 8 % 2 == 0, sqrt(8)
• So that’s a total of 10 evaluations to get the answer
• This is what the Stream implementation DOES NOT do:
• Look for all numbers greater than k in the list
• Look for all even numbers (greater than k) in the list
• Get the square root (of all even numbers greater than k) in the list
• The stream composes all intermediate operations
• It applies as many operations to each element first before moving on
• E.g. “is 2 > 4? Move on. Is 5 > 4? Is it even? Move on…”
This is essentially equivalent:
return numbers.stream()
.filter(num -> num > k && num % 2 == 0)
.map(Math::sqrt)
.findFirst();
So a Stream is not only more readable and declarative (what to do, not
how to do it) but it does this as efficiently as the imperative style.
Handling the result
• What if the list contains no even numbers?
• What if the list contains no numbers greater than k?
• What if the list is empty?
• Imperative style:
• return some arbitrary double value
• Could be misinterpreted– no way to know that it couldn’t find a value!
• Nobody likes handling exceptions!
• Functional style:
• return an OptionalDouble
Optional<T>
• An immutable container which may or may not be null
• Calling get() if no value is present will throw an unchecked exception
• Can get an Optional from static methods:
• Optional.of(T value) throws NullPointerException
• Optional.ofNullable(T value)
• Optional.empty()
• Can apply operations based on whether value is present
• ifPresent(Consumer<? super T> consumer)
• map and filter
• T orElse(T other), T orElseGet(Supplier<> other), orElseThrow
Optional chaining example
String customerNameByID(List<Customer> customers, int id) {
return customers.stream()
.filter(c -> c.getID() == id)
.findFirst() //Optional<Customer>
.map(Customer::getName)
.filter(Customer::isValidName)
.orElse(“UNKNOWN”);
}
If customer with id exists, return their name. Otherwise return something else.
Infinite Stream example
long totals = LongStream.generate(() ->
currentTimeMillis() % 1000)
.parallel()
.limit(1_000_000)
.sum();
• This will continuously generate numbers according to the logic
provided in the Supplier and add them up
• If you remove the limit, it’ll max out your CPU forever!
• The stream of numbers isn’t stored – it’s computed on-demand and
fed through the processing pipeline
Parallelism and Asynchronicity
• Parallel streams use the common ForkJoinPool by default
• Uses (number of logical cores - 1) threads
• Can easily get the computation to be handled by another pool
Future<Long> busyWork = new ForkJoinPool(numThreads)
.submit(getComputation(256));
...//do other busy work in the current thread
long execTime = busyWork.get(); //Blocked!
static Callable<Long> getComputation(final int target) {
return () -> {
final long startTime = nanoTime();
IntStream.range(0, target)
.parallel().forEach(loop -> {
int result = ThreadLocalRandom.current()
.ints(0, 2147483647)
.filter(i -> i <= target)
.findAny().getAsInt();
System.out.println(result);
});
return nanoTime()-startTime;
};
}
Triggering computations
• So far, we have seen parallel but synchronous (blocking)
• Even though our computation starts on submission to the Executor, we don’t
know when it’s done
• Calling .get() forces us to wait for the computation to finish
• Can use a ScheduledExecutorService to periodically execute (or
delay execution of) a ScheduledFuture
• But we want to have more flexibility and automation, not delay tasks
• Wouldn’t it be great if we could declare our computations and chain
them together to trigger automatically when they’re done?
• This could be used to handle dependencies between tasks, for example
CompletionStage<T>
• Complex interface (38 methods) for chaining computations
• Computation type may be Function, Consumer or Runnable
• May be triggered by one or two stages (including both and either)
• Execution can be default, async or async with custom Executor
• Flexible exception handling semantics
• BiConsumer/BiFunction where either result or exception is null
• Specify an exception handling function for this stage
• Exceptional completion is propagated downstream (to dependencies)
• toCompletableFuture() returns an interoperable implementation
CompletableFuture<T>
• implements CompletionStage<T>, Future<T> (total 59 methods!)
• Provides a non-blocking API for Future using callbacks
• Cancellation results in exceptional completion
• Can be explicitly completed (or exceptionally completed)
• T getNow(T valueIfAbsent) – non-blocking, returns fallback if absent
• Multiple ways to obtain a CompletableFuture
• supplyAsync, runAsync, completedFuture or no-args constructor
• Can combine any number of CompletableFuture to trigger completion
• static CompletableFuture<Void> allOf(CompletableFuture... cfs)
• static CompletableFuture<Object> anyOf(CompletableFuture... cfs)
CompletableFuture<Void> promise = CompletableFuture
.supplyAsync(() -> compute(target), executor)
.thenApply(Duration::ofNanos)
.thenApply(PerformanceTest::formatDuration)
.thenAccept(System.out::println)
.exceptionally(System.err::println);
for (boolean b = true; !promise.isDone(); b = !b) {
out.println(b ? "tick" : "tock");
Thread.sleep(1000);
}
Nashorn (JavaScript in Java)
• Java-based JavaScript engine (replaces Rhino)
• jjs (terminal command) gives you a JavaScript REPL (like nodejs)
• Can invoke and evaluate JS directly from Java (file or string)
• You can even get the result of the last expression as a native Java object!
• Everything in Nashorn is based on Java objects, so can be passed around
• Can invoke functions/methods written in JS from Java (and vice-versa)!
• Can bind variables into global JS space from Java
• Essentially allows us to write prettier code using JS which can
seamlessly interoperate with existing Java codebase
Other additions/changes
• new Date-Time API (java.time) – completely immutable
• Default methods added to various interfaces (e.g. in Comparator)
• Streams and lambdas used/added to various places in API
• e.g. concurrency utilities, File I/O as well as Collections
• Unsigned arithmetic support
• Parallel array sorting
• Various other back-end improvements (boring stuff)
Java 9 overview
• Modules (“Project Jigsaw”)
• Process API updates
• jshell: command-line REPL for Java
• Stack-walking API – standardized way to get info from stack traces
• HTTP 2.0 client, Money and Currency API, Reactive Streams
• private methods in interfaces?!
• Underscore (“_”) no longer a valid identifier
• Collection factory methods (immutable)
• Native desktop integration (java.awt.desktop)
Possible features in future versions of Java
• Data classes
• Many POJOs are just “dumb data holders”
• Scala-style class declarations would be better – no boilerplate
• Enhanced switch
• Test for types instead of lots of instanceof checks
• Get results from switch – case statements could return values
• Switch on data type patterns
• Project Valhalla – value types with no identity
• Don’t pay for what you don’t use – objects have unnecessary overhead
• “Codes like a class, works like an int” – exploit caching/locality
Questions?
• Thank you for listening!

A brief tour of modern Java

  • 1.
    A brief tourof modern Java by Sina Madani
  • 2.
    A brief history… •February 1997 – Java 1.1 AWT, inner classes, JDBC, reflection (introspection only), RMI, JavaBeans… • December 1998 – Java 2 • JIT compiler, Collections framework, JIT compiler, CORBA IDL… • May 2000 – Java 3 • Hotspot, JNDI, JDPA… • February 2002 – Java 4 • assert keyword, NIO, exception chaining, IPv6, prefs, APIs for security, logging, image…
  • 3.
    A brief history… •September 2004 – Java 5 (Java as we know it!) • Generics, annotations, enum, varargs, for-each, static imports, Scanner, auto-boxing/unboxing, concurrent utilities (task scheduling, atomics, locks…) • December 2006 – Java 6 (one of the most popular versions) • General back-end improvements, nothing new that’s noteworthy • July 2011 – Java 7 (Project Coin) • invokedynamic, Strings in switch, type inference (“diamonds”), binary literals, try-with-resources, fork/join framework, multi-catch, new file I/O… • March 2014 – Java 8 • July 2017 (we hope!) – Java 9
  • 4.
    Why is Java8 so significant? • Like Java 5, Java 8 makes fundamental additions to the language • It (should!) change how programmers think and code • It embraces functional programming • It brings Java to the modern era • Java is “plagued” by its origins • Lots of “modern” JVM languages are far superior: Groovy, Scala, Kotlin… • Java 8 makes significant strides to keep up to date
  • 5.
    default and staticmethods in interfaces • Interface methods can contain a default implementation • Implementing classes do not need to override default methods • “Poor man’s multiple inheritance”? • Partially removes the need for X as interface and AbstractX • A convenient way to make it less laborious to implement interfaces • Allows interfaces to evolve without breaking compatibility • Static methods also allow for evolution – no longer need to have a wrapper class for implementing utility methods • Partially motivated by other new features in Java 8
  • 6.
    Diamond problem interface I1{ default void foo() { System.out.println(“I1::foo”); } } interface I2 { default void foo() { System.out.println(“I2::foo”); } }
  • 7.
    class BC1 { publicvoid foo() { System.out.println(“BC1::foo”); } } class Derived extends BC1 implements I1, I2 {} • Calling new Derived().foo() results in “BC1::foo”
  • 8.
    • What ifyou have: class Derived implements I1, I2 {} new Derived().foo(); • This won’t compile – which foo() do we mean? • Need to implement it to resolve this issue • Can still call the base implementations if we want class Derived implements I1, I2 { public void foo() { System.out.println(“Derived::foo”); I1.super.foo(); I2.super.foo(); } }
  • 9.
    Functional interfaces • Afunctional interface has exactly one non-default method • Runnable is a functional interface – just one method void run() • An interface with one (unimplemented) method is really a function • Think about it: when you pass a Runnable to a Thread, you are really telling the thread to execute the body of the run() method, and nothing else • Due to Java’s principle of “everything has to be an object”, functions haven’t been treated with the dignity they deserve. • Functional interfaces promote functions to “first-class” citizens • “Hey daddy, can I go alone?” “No, you need to be wrapped in a class.” • This is no longer the case with Java 8
  • 10.
    Lambda expressions • Thisis the “headline” feature of Java 8 (deservedly so!) • A replacement for anonymous functional interface implementations • You no longer need the “ceremony” for doing this common task • A lambda expression allows you to define the body of a functional interface’s method without all the boilerplate around it
  • 11.
    Before Java 8 Threadt = new Thread(new Runnable() { @Override public void run() { System.out.print(currentThread().getName()); } });
  • 12.
    With lambdas Thread t= new Thread (() -> { System.out.print(currentThread().getName()); });
  • 13.
    Method references • Evenmore concise than lambdas • Suppose all you want to do is pass the parameters to another method • E.g. a Runnable in ThreadFactory • With lambdas, this is as follows: ThreadFactory tf = new ThreadFactory(r -> { return new Thread(r); });
  • 14.
    ThreadFactory before Java8 ThreadFactory tf = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r); } };
  • 15.
    ThreadFactory with methodreference ThreadFactory tf = Thread::new;
  • 16.
    Things to knowwith lambdas • Lambdas are not simply syntactic sugar for anonymous inner classes • No .class files are generated – dynamic dispatch used instead • Variables of the outer class accessed within the body of lambda expression should be (effectively) final • That is, you can’t change the value from within the lambda expression • Their value must be assigned – need to be statically determinable • You can’t throw checked exceptions from within a lambda • Exceptions need to be handled within the lambda • There are ways to “cheat” this using wrappers
  • 17.
    java.util.function • This iswhat drives lambda expressions and method references • Function<T,R> R apply(T t); • Consumer<T> void accept(T t); • Supplier<T> T get(); • Predicate<T> boolean test(T t); • UnaryOperator<T> T apply(T t); • Also has function types for every combination of int, long, double • E.g. IntConsumer, IntToLongFunction, DoubleToIntConsumer etc. • Every interface also has a “Bi” version – accepts two parameters • e.g. BiFunction<T,U,R> R apply(T t, U u);
  • 18.
    Example: Collections.forEach • CollectionsAPI (java.util.Collection and friends) has been updated to take advantage of the functional interfaces • One of the most common tasks is to iterate over a collection Arrays.asList(5, 7, 9, 16).forEach(System.out::println); • forEach expects a Consumer<T> where T is the type of the collection
  • 19.
    Streams API (java.util.stream) •A Stream is an abstract pipeline of computation • Allows for performing aggregate operations on data sources • A data source could be a collection, arrays, iterators, I/O etc. • java.util.stream only consists of interfaces, not implementation • Includes Stream<T>, IntStream, DoubleStream, LongStream etc. • Common operations include: • map, reduce, filter, findAny, findFirst, forEach, count, distinct, iterate, generate, limit, min, max, sorted, skip, peek, of, noneMatch, allMatch, anyMatch, collect, toArray
  • 20.
    Properties of Streams •Lazily evaluated • Most (“intermediate”) operations just build up the pipeline of computation • Only “terminal operations” will trigger any actual “work” • Terminal operations include forEach, collect, findAny, sum, reduce etc. • Streams do not store any data; nor do they modify the source • Always return a new Stream for intermediate operations • Streams are consumable – traverse the data source only once • Single-pass pipeline and laziness make streams very efficient • Can be unbounded (infinite streams) • A convenient way to express data-parallel computations
  • 21.
    Language evolution withexample • Suppose you have a set of names • Create a capitalised subset of the names shorter than 5 characters E.g. [“Sarah”, “Beth”, “Hamid”, “Marcus”, “Chris”, “Jim”] becomes [“BETH”, “CHRIS”, “JIM”]
  • 22.
    Java 1 Set getShortNames(Setnames) { Set shortNames = new HashSet(); Iterator iter = names.iterator(); while (iter.hasNext()) { String name = (String) iter.next(); if (name.length() < 5) shortNames.add(name.toUpperCase()); } return shortNames; }
  • 23.
    Java 5 Set<String> getShortNames(Set<String>names) { Set<String> shortNames = new HashSet<String>(); for (String name : names) { if (name.length() < 5) shortNames.add(name.toUpperCase()); } return shortNames; }
  • 24.
    Java 8 Set<String> getShortNames(Set<String>names) { return names.stream() .filter(name -> name.length() < 5) .map(String::toUpperCase) .collect(Collectors.toSet()); }
  • 25.
    Extension 1 • Getthe resulting set of names as a single string separated by commas • For example: Set [“Chris”, “Charlie”, “Megan”, “Amy”, “Ben”, “Zoe”] becomes: String “CHRIS, AMY, BEN, ZOE”
  • 26.
    Imperative style (Java7) String getShortNames(Set<String> names) { StringBuilder shortNames = new StringBuilder(); Iterator iter = names.iterator(); for (int i = 0; i < iter.hasNext(); i++) { String name = iter.next(); if (name.length() < 5) shortNames.append(name.toUpperCase()); if (i != names.size()) shortNames.append(“, ”); } return shortNames.toString(); }
  • 27.
    Declarative style (Java8) String getShortNames(Set<String> names) { return names.stream() .filter(name -> name.length() < 5) .map(String::toUpperCase) .collect(Collectors.joining(“, ”)); }
  • 28.
    Extension 2 • Theset of names has > 1 million elements • We need to process them faster • Create a multi-threaded version to speed things up
  • 29.
    Java 1 • “I‘mhanding in my resignation.”
  • 30.
    Java 5 • “Sorryboss, I’m not feeling well today.”
  • 31.
    Java 8 Set<String> getShortNames(Set<String>names) { return names.parallelStream() .filter(name -> name.length() < 5) .map(String::toUpperCase) .collect(Collectors.toSet()); }
  • 32.
    Efficiency example “Find thesquare root of the first even number greater than k.” Suppose: List<Integer> numbers = asList(1, 2, 5, 3, 7, 8, 12, 10); and k = 4
  • 33.
    Imperative style double result= 0d; for (number : numbers) { if (number > k && number % 2 == 0) { result = Math.sqrt(number); break; } } return result;
  • 34.
    Stream (functional style) returnnumbers.stream() .filter(number -> number > k) .filter(number -> number % 2 == 0) .map(Math::sqrt) .findFirst();
  • 35.
    Which does morework? • Imperative style: • 1 < 4, 2 < 4, 5 > 4, 5 % 2 > 0, 3 < 4, 7 > 4, 7 % 2 > 0, 8 > 4, 8 % 2 == 0, sqrt(8) • So that’s a total of 10 evaluations to get the answer • This is what the Stream implementation DOES NOT do: • Look for all numbers greater than k in the list • Look for all even numbers (greater than k) in the list • Get the square root (of all even numbers greater than k) in the list • The stream composes all intermediate operations • It applies as many operations to each element first before moving on • E.g. “is 2 > 4? Move on. Is 5 > 4? Is it even? Move on…”
  • 36.
    This is essentiallyequivalent: return numbers.stream() .filter(num -> num > k && num % 2 == 0) .map(Math::sqrt) .findFirst(); So a Stream is not only more readable and declarative (what to do, not how to do it) but it does this as efficiently as the imperative style.
  • 37.
    Handling the result •What if the list contains no even numbers? • What if the list contains no numbers greater than k? • What if the list is empty? • Imperative style: • return some arbitrary double value • Could be misinterpreted– no way to know that it couldn’t find a value! • Nobody likes handling exceptions! • Functional style: • return an OptionalDouble
  • 38.
    Optional<T> • An immutablecontainer which may or may not be null • Calling get() if no value is present will throw an unchecked exception • Can get an Optional from static methods: • Optional.of(T value) throws NullPointerException • Optional.ofNullable(T value) • Optional.empty() • Can apply operations based on whether value is present • ifPresent(Consumer<? super T> consumer) • map and filter • T orElse(T other), T orElseGet(Supplier<> other), orElseThrow
  • 39.
    Optional chaining example StringcustomerNameByID(List<Customer> customers, int id) { return customers.stream() .filter(c -> c.getID() == id) .findFirst() //Optional<Customer> .map(Customer::getName) .filter(Customer::isValidName) .orElse(“UNKNOWN”); } If customer with id exists, return their name. Otherwise return something else.
  • 40.
    Infinite Stream example longtotals = LongStream.generate(() -> currentTimeMillis() % 1000) .parallel() .limit(1_000_000) .sum(); • This will continuously generate numbers according to the logic provided in the Supplier and add them up • If you remove the limit, it’ll max out your CPU forever! • The stream of numbers isn’t stored – it’s computed on-demand and fed through the processing pipeline
  • 41.
    Parallelism and Asynchronicity •Parallel streams use the common ForkJoinPool by default • Uses (number of logical cores - 1) threads • Can easily get the computation to be handled by another pool Future<Long> busyWork = new ForkJoinPool(numThreads) .submit(getComputation(256)); ...//do other busy work in the current thread long execTime = busyWork.get(); //Blocked!
  • 42.
    static Callable<Long> getComputation(finalint target) { return () -> { final long startTime = nanoTime(); IntStream.range(0, target) .parallel().forEach(loop -> { int result = ThreadLocalRandom.current() .ints(0, 2147483647) .filter(i -> i <= target) .findAny().getAsInt(); System.out.println(result); }); return nanoTime()-startTime; }; }
  • 43.
    Triggering computations • Sofar, we have seen parallel but synchronous (blocking) • Even though our computation starts on submission to the Executor, we don’t know when it’s done • Calling .get() forces us to wait for the computation to finish • Can use a ScheduledExecutorService to periodically execute (or delay execution of) a ScheduledFuture • But we want to have more flexibility and automation, not delay tasks • Wouldn’t it be great if we could declare our computations and chain them together to trigger automatically when they’re done? • This could be used to handle dependencies between tasks, for example
  • 44.
    CompletionStage<T> • Complex interface(38 methods) for chaining computations • Computation type may be Function, Consumer or Runnable • May be triggered by one or two stages (including both and either) • Execution can be default, async or async with custom Executor • Flexible exception handling semantics • BiConsumer/BiFunction where either result or exception is null • Specify an exception handling function for this stage • Exceptional completion is propagated downstream (to dependencies) • toCompletableFuture() returns an interoperable implementation
  • 45.
    CompletableFuture<T> • implements CompletionStage<T>,Future<T> (total 59 methods!) • Provides a non-blocking API for Future using callbacks • Cancellation results in exceptional completion • Can be explicitly completed (or exceptionally completed) • T getNow(T valueIfAbsent) – non-blocking, returns fallback if absent • Multiple ways to obtain a CompletableFuture • supplyAsync, runAsync, completedFuture or no-args constructor • Can combine any number of CompletableFuture to trigger completion • static CompletableFuture<Void> allOf(CompletableFuture... cfs) • static CompletableFuture<Object> anyOf(CompletableFuture... cfs)
  • 46.
    CompletableFuture<Void> promise =CompletableFuture .supplyAsync(() -> compute(target), executor) .thenApply(Duration::ofNanos) .thenApply(PerformanceTest::formatDuration) .thenAccept(System.out::println) .exceptionally(System.err::println); for (boolean b = true; !promise.isDone(); b = !b) { out.println(b ? "tick" : "tock"); Thread.sleep(1000); }
  • 47.
    Nashorn (JavaScript inJava) • Java-based JavaScript engine (replaces Rhino) • jjs (terminal command) gives you a JavaScript REPL (like nodejs) • Can invoke and evaluate JS directly from Java (file or string) • You can even get the result of the last expression as a native Java object! • Everything in Nashorn is based on Java objects, so can be passed around • Can invoke functions/methods written in JS from Java (and vice-versa)! • Can bind variables into global JS space from Java • Essentially allows us to write prettier code using JS which can seamlessly interoperate with existing Java codebase
  • 48.
    Other additions/changes • newDate-Time API (java.time) – completely immutable • Default methods added to various interfaces (e.g. in Comparator) • Streams and lambdas used/added to various places in API • e.g. concurrency utilities, File I/O as well as Collections • Unsigned arithmetic support • Parallel array sorting • Various other back-end improvements (boring stuff)
  • 49.
    Java 9 overview •Modules (“Project Jigsaw”) • Process API updates • jshell: command-line REPL for Java • Stack-walking API – standardized way to get info from stack traces • HTTP 2.0 client, Money and Currency API, Reactive Streams • private methods in interfaces?! • Underscore (“_”) no longer a valid identifier • Collection factory methods (immutable) • Native desktop integration (java.awt.desktop)
  • 50.
    Possible features infuture versions of Java • Data classes • Many POJOs are just “dumb data holders” • Scala-style class declarations would be better – no boilerplate • Enhanced switch • Test for types instead of lots of instanceof checks • Get results from switch – case statements could return values • Switch on data type patterns • Project Valhalla – value types with no identity • Don’t pay for what you don’t use – objects have unnecessary overhead • “Codes like a class, works like an int” – exploit caching/locality
  • 51.

Editor's Notes

  • #2 http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html http://openjdk.java.net/projects/jdk9/
  • #3 https://en.wikipedia.org/wiki/Java_version_history
  • #5 “Plague” Architects admit they’d have done things differently if they could go back in time Design heavily influenced by what was going on in 1995 Need to “steal” C++ programmers Familiar syntax and constructs Promote OO Worst HelloWorld ever? (too much ceremony) Mutability is the default Checked exceptions Single inheritance “Generics” are tacked on No operator overloading No value types (like structs)
  • #6 https://youtu.be/GphO9fWhlAg
  • #7 https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
  • #11 https://youtu.be/1OpAgZvYXLQ
  • #17 http://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8-streams
  • #18 https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
  • #20 https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
  • #21 https://youtu.be/1OpAgZvYXLQ?t=2h5m
  • #36 [1, 2, 5, 3, 7, 8, 12, 10], k=4
  • #37 Short-circuited
  • #39 https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
  • #40 https://youtu.be/Ej0sss6cq14
  • #42 submit takes a Callable<V> and returns a ForkJoinTask<V> ForkJoinTask implements Future
  • #43 Easy way to do “embarrassingly parallel” for-loops
  • #44 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html
  • #45 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html CompletionStage interface has 38 methods determined in a (3x3x3)+(3x3+1) + 1 “toCompleteableFuture()” The first aspect (what triggers a stage): Methods with names starting with “then” are for adding another stage to be triggered when a single stage completes. Methods with names containing “both” are for adding another stage to be triggered when two previous stages both complete. Methods with names containing “either” are for adding another stage to be triggered when either one of two previous stages completes. The second aspect (whether the computation takes an argument and returns a result): Methods with names containing “apply” take a Function, which takes an argument (the result of the previous stage) and return a result (the argument for the next stage). Methods with names containing “accept” take a Consumer, which takes an argument but does not return a result. Methods with names containing “run” take a Runnable, which takes no arguments and does not return a result. The third aspect (how the execution of the computation is arranged): Methods with names which do not end in “async” execute the computation using the stage’s default execution facility. Methods with names that end in “async” execute the computation using the stage’s default asynchronous execution facility. Methods with names that end in “async” and that also take an Executor argument, execute the computation using the specified Executor. Lastly, there four handler methods for dealing with exceptions and/or more general “meta-level” aspects of computations http://www.jesperdj.com/2015/09/26/the-future-is-completable-in-java-8/
  • #46 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html https://youtu.be/-MBPQ7NIL_Y?t=18m https://youtu.be/HdnHmbFg_hw
  • #47 https://blog.krecan.net/2013/12/25/completablefutures-why-to-use-async-methods/ http://fahdshariff.blogspot.co.uk/2016/06/java-8-completablefuture-vs-parallel.html
  • #48 http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/ https://youtu.be/lZgbTAZwerc?t=21m
  • #49 http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
  • #50 https://www.sitepoint.com/ultimate-guide-to-java-9/ https://youtu.be/fxB9cVNcyZo http://openjdk.java.net/projects/jdk9/ http://openjdk.java.net/jeps/261 http://openjdk.java.net/jeps/259 http://openjdk.java.net/jeps/213 https://adtmag.com/blogs/watersworks/2015/08/java-9-hack.aspx
  • #51 https://youtu.be/oGll155-vuQ?t=22m – fancy switch https://youtu.be/oGll155-vuQ?t=32m22s – Value types Would be nice to have default parameters instead of having to make factory methods and lots of constructors