a brief tour of modern java

51
A brief tour of modern Java by Sina Madani

Upload: sina-madani

Post on 22-Jan-2018

97 views

Category:

Software


0 download

TRANSCRIPT

Page 1: A brief tour of modern Java

A brief tour of modern Javaby Sina Madani

Page 2: A brief tour of modern Java

A brief history…

• February 1997 – Java 1.1AWT, 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…

Page 3: A brief tour of modern Java

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

Page 4: A brief tour of modern Java

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

Page 5: A brief tour of modern Java

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

Page 6: A brief tour of modern Java

Diamond problem

interface I1 {

default void foo() {

System.out.println(“I1::foo”);

}

}

interface I2 {

default void foo() {

System.out.println(“I2::foo”);

}

}

Page 7: A brief tour of modern Java

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”

Page 8: A brief tour of modern Java

• 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();

}

}

Page 9: A brief tour of modern Java

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

Page 10: A brief tour of modern Java

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

Page 11: A brief tour of modern Java

Before Java 8

Thread t = new Thread(new Runnable() {

@Override

public void run() {

System.out.print(currentThread().getName());

}

});

Page 12: A brief tour of modern Java

With lambdas

Thread t = new Thread (() -> {

System.out.print(currentThread().getName());

});

Page 13: A brief tour of modern Java

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);

});

Page 14: A brief tour of modern Java

ThreadFactory before Java 8

ThreadFactory tf = new ThreadFactory() {

@Override

public Thread newThread(Runnable r) {

return new Thread(r);

}

};

Page 15: A brief tour of modern Java

ThreadFactory with method reference

ThreadFactory tf = Thread::new;

Page 16: A brief tour of modern Java

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

Page 17: A brief tour of modern Java

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);

Page 18: A brief tour of modern Java

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

Page 19: A brief tour of modern Java

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

Page 20: A brief tour of modern Java

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

Page 21: A brief tour of modern Java

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”]

Page 22: A brief tour of modern Java

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;

}

Page 23: A brief tour of modern Java

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;

}

Page 24: A brief tour of modern Java

Java 8

Set<String> getShortNames(Set<String> names) {

return names.stream()

.filter(name -> name.length() < 5)

.map(String::toUpperCase)

.collect(Collectors.toSet());

}

Page 25: A brief tour of modern Java

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”

Page 26: A brief tour of modern Java

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();

}

Page 27: A brief tour of modern Java

Declarative style (Java 8)

String getShortNames(Set<String> names) {

return names.stream()

.filter(name -> name.length() < 5)

.map(String::toUpperCase)

.collect(Collectors.joining(“, ”));

}

Page 28: A brief tour of modern Java

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

Page 29: A brief tour of modern Java

Java 1

• “I‘m handing in my resignation.”

Page 30: A brief tour of modern Java

Java 5

• “Sorry boss, I’m not feeling well today.”

Page 31: A brief tour of modern Java

Java 8

Set<String> getShortNames(Set<String> names) {

return names.parallelStream()

.filter(name -> name.length() < 5)

.map(String::toUpperCase)

.collect(Collectors.toSet());

}

Page 32: A brief tour of modern Java

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

Page 33: A brief tour of modern Java

Imperative style

double result = 0d;

for (number : numbers) {

if (number > k && number % 2 == 0) {

result = Math.sqrt(number);

break;

}

}

return result;

Page 34: A brief tour of modern Java

Stream (functional style)

return numbers.stream()

.filter(number -> number > k)

.filter(number -> number % 2 == 0)

.map(Math::sqrt)

.findFirst();

Page 35: A brief tour of modern Java

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…”

Page 36: A brief tour of modern Java

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.

Page 37: A brief tour of modern Java

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

Page 38: A brief tour of modern Java

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

Page 39: A brief tour of modern Java

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.

Page 40: A brief tour of modern Java

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

Page 41: A brief tour of modern Java

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!

Page 42: A brief tour of modern Java

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;

};

}

Page 43: A brief tour of modern Java

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

Page 44: A brief tour of modern Java

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

Page 45: A brief tour of modern Java

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)

Page 46: A brief tour of modern Java

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);

}

Page 47: A brief tour of modern Java

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

Page 48: A brief tour of modern Java

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)

Page 49: A brief tour of modern Java

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)

Page 50: A brief tour of modern Java

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

Page 51: A brief tour of modern Java

Questions?

• Thank you for listening!