Learning Java 8, Lambda

Preparation for OCJP8 Exam

Andy Chu

OCJP 8 Upgrade Exam Topic

http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=5001&get_params=p_exam_id:1Z0-810

Overview

  • Lambda Expression
  • Using Built in Lambda Types 
  • Filtering Collections with Lambdas
  • Collection Operation with Lambda
  • Parallel Streams
  • Lambda Cookbook
  • Method Enhancements
  • Use Java SE 8 Date/Time API
  • JavaScript on Java with Nashorn

Lambda Expression

  • ​Describe/use Java Inner Class
  • Define/write functional Interface
  • Refactor code from anonymous inner class to lambda expression

Describe/use Java Inner Class

// filename: OuterClass.java
public class OuterClass {
	
	static class InnerClass{}
	
	class InnerClass2{}
	
	public void doStuff(Object obj){
		class InnerClass3{}
	}
	
	public static void main(String[] args){
		new OuterClass().doStuff(new InnerClass4(){});
	}
}

class InnerClass4{}

static class InnerClass5{}

private class InnerClass6{}

Which line(s) causes error?

Describe/use Java Inner Class

public static void main(String[] args){
	new OuterClass().doStuff(new InnerClass4(){});
	new InnerClass();
	new OuterClass().new InnerClass2();
	new InnerClass4();
}

Which line(s) causes error?

Describe/use Java Inner Class

public class EffecivelyFinal {
	
	interface InnerClass{void print();}
	public void doStuff(InnerClass o){}
	
	public static void main(String[] args){
		final int x = 5;
		int y = 10;
		new EffecivelyFinal().doStuff(new InnerClass() {
			@Override
			public void print() {
				System.out.println(x);
				System.out.println(y);
			}
		});
		y = 15;
		new EffecivelyFinal().doStuff(new InnerClass() {
			@Override
			public void print() {
				System.out.println(y);
			}
		});
	}
}

Which line(s) causes error?

Define/write functional Interface

  • what is interface
  • interface vs abstract
  • when to use interface
  • interface vs functional interface
  • when to use functional interface

Define/write functional Interface

functional interface

  • similar as interface
  • but only accept single public abstract method
@FunctionalInterface
public interface FuncInterface {
	void method();
	default void func(){}
}

Define/write functional Interface

public class FuncInterfaceEx {
	
	interface Foo{ void foo(); }
	
	@FunctionalInterface
	interface Foo2{ void foo(); }
	
	@FunctionalInterface
	interface Foo3{ static void foo();}
	
	@FunctionalInterface
	interface Foo4 extends Foo3{ void foo();}
	
	@FunctionalInterface
	interface Foo5 { default void foo(){};}
	
	@FunctionalInterface
	interface Foo6 extends Foo5 { void foo();}
	
	interface Foo7 { default void foo();}
	
	@FunctionalInterface
	interface Foo8 extends Foo6 { void foo();}
	
}

Which line(s) causes error?

Refactor code from anonymous inner class to lambda expression

What is lambda??

y = f(x) = x + 1

 

if x = 0 ... 9

then y = 1...10

 

anonymous class

Refactor code from anonymous inner class to lambda expression

interface OnWhateverListener{ void onWhatever(); }
	
public void doStuff(OnWhateverListener listener){
	listener.onWhatever();
}
	
public static void main(String[] args){
	new AnonymousClass().doStuff(new OnWhateverListener() {
		@Override
		public void onWhatever() {
			// do whatever stuff
		}
	});
}

anonymous class 

public static void main(String[] args) {
	new AnonymousClass().doStuff(() -> {
		// do whatever stuff
		});
}

lambda expression

Refactor code from anonymous inner class to lambda expression

f(x) = x + 1

new AnonymousClass().doStuff(new OnWhateverListener() {
	@Override
	public void onWhatever() {
		// do whatever stuff
	}
});
new Thread(new Runnable() {
	@Override
	public void run() {
		// do something
	}
});

INPUT

OUTPUT

Functional Interface

anonymous class 

Refactor code from anonymous inner class to lambda expression

@FunctionalInterface 
interface NoArg{ void foo(); }

Functional Interface

Anonymous class 

as Lambda expression

() -> System.out.println("")
@FunctionalInterface 
interface OneArg{ void foo(String s); }
s -> System.out.println(s)
s -> {
    s = s.trim();
    System.out.println(s);
}
@FunctionalInterface 
interface TwoArg{ void foo(int x, int y); }
(x, y) -> x + y
(int x, int y) -> x + y

Refactor code from anonymous inner class to lambda expression

public class LambdaEx {
	@FunctionalInterface 
	interface NoArg{ void foo(); }

	@FunctionalInterface 
	interface OneArg{ void foo(String s); }
	
	@FunctionalInterface 
	interface TwoArg{ void foo(int x, int y); }
	
	public void perform(NoArg foo){}
	public void perform(TwoArg foo){}
	public void perform(OneArg foo){}

	public static void main(String[] args) {
		LambdaEx obj = new LambdaEx();
		obj.perform(()->System.out.println(""));
		obj.perform(s->System.out.println(s));
		obj.perform((x,y)->System.out.println(x+y));
		obj.perform((int x,int y)->System.out.println(x+y));
	}

}

Refactor code from anonymous inner class to lambda expression

 

Think of 

JavaScript call, apply functions

Android observer pattern

Thread runnable 

Concurrent Future callable

Reactive programming

Strategy pattern

Command object / pattern

Spring IoC

 

Reference

  • http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
  • http://en.wikipedia.org/wiki/Anonymous_function#Java
  • http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
  • http://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8
  • http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Using Built in Lambda Types

  • Describe java.util.function package
  • Develop/uses
    • Function interface 
    • Consumer interface 
    • Supplier interface 
    • UnaryOperator interface 
    • Predicate interface
  • Develop/use primitive and binary variations of base interface
  • Develop/use method reference

java.util.function package

INPUT

OUTPUT

Functional Interface

T

Void

Consumer

n/a

T

Suppiler

T

R

Function

T

boolean

Predicate

T

T

UnaryOperator

T, T

T

BinaryOperator

anonymous class 

java.util.function package

T

Void

Consumer

public class ConsumerEx{
	
	interface Consumer<T>{
		void perform(T t);
	}
	public void doStuff(Consumer<String> c){}
	
	public void withJava() {
		doStuff(new Consumer<String>(){
			@Override
			public void perform(String t) {
				System.out.println(t);
			}
		});
	}
	public void withJava8() {
		doStuff(s -> System.out.println(s.toLowerCase()));
		doStuff(s -> System.out.println(s.toUpperCase()));
	}
}

java.util.function package

n/a

T

Supplier

public class SupplierEx{
	
	interface Supplier<T>{
		T perform();
	}
	public void doStuff(Supplier<String> s){
		System.out.println(s.perform());
	}
	
	public void withJava() {
		doStuff(new Supplier<String>(){
			@Override
			public String perform() {
				return "Hello World";
			}
		});
	}
	public void withJava8() {
		doStuff(() -> "Hello World".toLowerCase());
		doStuff(() -> {return "Hello World".toUpperCase();});
	}
}

java.util.function package

T

boolean

Predicate

public class PredicateEx{
	
	interface Predicate<T>{
		boolean perform(T t);
	}
	public void doStuff(Predicate<String> p){
		String s = "something";
		if(p.perform(s)){System.out.println("do" + s);}
	}
	
	public void withJava() {
		doStuff(new Predicate<String>(){
			@Override
			public boolean perform(String s) {
				return s != null;
			}
		});
	}
	public void withJava8() {
		doStuff(s -> s != null);
	}
}

java.util.function package

T

R

Function

public class FunctionEx{
	
	interface Function<T, R>{
		R perform(T t);
	}
	public void doStuff(Function<Integer, String> f){
		System.out.println(f.perform((int)Math.random() * 100));
	}
	
	public void withJava() {
		doStuff(new Function<Integer, String>(){
			@Override
			public String perform(Integer s) {
				if(s >= 60) return "pass";
				else return "fail";
			}
		});
	}
	public void withJava8() {
		doStuff(score -> {
			if(score >= 80) return "pass";
			else return "fail";
		});
	}
}

java.util.function package

T

T

UnaryOperator

public class UnaryOpEx{
	
	interface UnaryOp<T>{
		T perform(T t);
	}
	public void doStuff(UnaryOp<String> u){
		System.out.println(u.perform("something"));
	}
	
	public void withJava() {
		doStuff(new UnaryOp<String>(){
			@Override
			public String perform(String s) {
				return "baked " + s;
			}
		});
	}
	public void withJava8() {
		doStuff(s -> "baked " + s);
		doStuff(s -> "eat " + s);
	}
}

java.util.function package

T, T

T

BinaryOperator

public class BinaryOpEx{
	
	interface BinaryOp<T>{
		T perform(T t, T t2);
	}
	public void doStuff(BinaryOp<Integer> b){
		System.out.println(b.perform(2, 1));
	}
	
	public void withJava() {
		doStuff(new BinaryOp<Integer>(){
			@Override
			public Integer perform(Integer a, Integer b) {
				return a + b;
			}
		});
	}
	public void withJava8() {
		doStuff((a, b) -> a * b);
		doStuff((a, b) -> a - b);
	}
}

java.util.function package

https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

Develop/use primitive and binary variations of base interface

Function descriptor

T -> boolean

int -> boolean

double -> boolean

T -> R

int -> R

T -> int

Function Interface

Predicate<T>

IntPredicate

DoublePredicate

Function<T, R>

IntFunction<R>

ToIntFunction<T>

Develop/use primitive and binary variations of base interface

  • Predicate<Integer> vs IntPredicate
  • Java 1.5 introduce autoboxing, unboxing
  • autoboxing : int -> Integer
  • unboxing: Integer -> int

Develop/use primitive and binary variations of base interface

public class IntPredicateEx{
	
	interface IntPredicate extends Predicate<Integer>{
		boolean perform(int t);
	}
	public void doStuff(IntPredicate p){
		if(p.perform((int) Math.random() * 10)){
			System.out.println("Hello World");
		}
	}
	public void withJava() {
		doStuff(new IntPredicate(){
			@Override
			public boolean perform(int t) {
				return false;
			}
		});
	}
	public void withJava8() {
		doStuff(n -> true);
	}
}

Which line(s) causes error? How to correct?

Develop/use primitive and binary variations of base interface

public class IntPredicateEx{
	
        // this is not functional interface
	interface IntPredicate extends Predicate<Integer>{
		boolean perform(int t);
	}
	public void doStuff(IntPredicate p){
		if(p.perform((int) Math.random() * 10)){
			System.out.println("Hello World");
		}
	}
	public void withJava() {
                // needs to implement both because of autoboxing/unboxing 
		doStuff(new IntPredicate(){
			@Override
			public boolean perform(int t) {
				return false;
			}
                        @Override
			public boolean perform(Integer t) {
				return false;
			}
		});
	}
	public void withJava8() {
		doStuff(n -> true); // can't compile
	}
}

Develop/use method reference

ClassName::staticMethodName

// ClassName::staticMethodName
IntFunction<String> f = n -> String.valueOf(n);
IntFunction<String> f = String::valueOf;
// ClassName::new
Supplier<String> f = () -> new String();
Supplier<String> f = String::new;
// ClassName::instanceMethodName
UnaryOperator<String> f = s -> s.trim();
UnaryOperator<String> f = String::trim;

ClassName::new

ClassName::instanceMethodName

// object::instanceMethodName
Integer i = new Integer(1);
Supplier<String> f = () -> i.toString();
Supplier<String> f = i::toString;

object::instanceMethodName

Reference

  • http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

Filtering Collections with Lambdas

  • Develop/uses code that iterates a collection by using forEach
  • Describe the Stream interface and pipelines
  • Filter a collection using lambda expression
  • Identify lambda operation that are lazy

Develop/uses code that iterates a collection by using forEach

public class Apple {
	private String color;
	private int calory;
	
	public Apple(){
		this.color = "red";
		this.calory = 300;
	}
	
	public Apple(String color) {
		this.color = color;
		this.calory = 300;
	}
	
	public Apple(String color, int calory) {
		this.color = color;
		this.calory = calory;
	}
	
	public String getColor(){
		return color;
	}
	
	public int getCalory(){
		return calory;
	}
}

Apple.java

Develop/uses code that iterates a collection by using forEach

public class ExternalIteration{
	List<Apple> apples = new ArrayList<Apple>();
	
	public ExternalIteration(){
		apples.add(new Apple("green"));
		apples.add(new Apple("red"));
		apples.add(new Apple("red"));
	}
	
	public void withJava() {
		for(Apple apple : apples){
			if("green".equals(apple)){
				System.out.println("I found green apple.");
			}
		}
	}
}

(Active) External iteration

Develop/uses code that iterates a collection by using forEach

public class InternalIteration{
	List<Apple> apples = new ArrayList<Apple>();
	
	public InternalIteration(){
		apples.add(new Apple("green"));
		apples.add(new Apple("red"));
		apples.add(new Apple("red"));
	}
	
	public void withJava8(Consumer<Apple> c) {
		for(Apple apple : apples){
			c.accept(apple);
		}
	}
	
	public void doStuff(){
		withJava8(apple -> {
			if("green".equals(apple)){
				System.out.println("I found green apple.");
			}
		});
	}
}

(passive) Internal iteration

Develop/uses code that iterates a collection by using forEach

public interface Iterable<T> {
    public Iterator<T> iterator();
}

Java 5 

public interface Iterable<T extends Object> {

    public Iterator<T> iterator();

    public default void forEach(Consumer<? super T> cnsmr) {
        // ...
    }

    ...
}

Java 8 

Develop/uses code that iterates a collection by using forEach

public class Iteration{
	List<Apple> apples = new ArrayList<Apple>();
	
	public Iteration(){
		apples.add(new Apple("green"));
		apples.add(new Apple("red"));
		apples.add(new Apple("red"));
	}
	
	public void withJava8() {
		apples.forEach(apple -> {
			if("green".equals(apple)){
				System.out.println("I found green apple.");
			}
		});
	}
}

Collection, List, Set, Map

Describe the Stream interface and pipelines

public class Pipeline{
	List<Apple> apples = new ArrayList<Apple>();
	
	public Pipeline(){
		apples.add(new Apple("green", 200));
		apples.add(new Apple("red", 300));
		apples.add(new Apple("red",50));
	}
	
	public void withJava() {
		List<Apple> redApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if("red".equals(apple)){
				redApples.add(apple);
			}
		}

		List<Apple> lowFatRedApples = new ArrayList<Apple>();
		for(Apple apple : redApples){
			if(apple.getCalory() < 100){
				lowFatRedApples.add(apple);
			}
		}
	}
}

Find low fat and red apples

Describe the Stream interface and pipelines

public class Pipeline2{
	...
	
	public List<Apple> findRedApples(List<Apple> apples) {
		List<Apple> redApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if("red".equals(apple)){
				redApples.add(apple);
			}
		}
		return redApples;
	}
	
	public List<Apple> findLowFatApples(List<Apple> apples) {
		List<Apple> lowFatRedApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if(apple.getCalory() < 100){
				lowFatRedApples.add(apple);
			}
		}
		return lowFatRedApples;
	}
	
	public List<Apple> findLowFatAndRedApples(List<Apple> apples) {
		return findLowFatAndRedApples(findRedApples(apples));
	}
}

What pattern do you see here ?

Describe the Stream interface and pipelines

public class Pipeline3{
	...
	
	public Pipeline3 findRedApples() {
		List<Apple> redApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if("red".equals(apple)){
				redApples.add(apple);
			}
		}
		apples = redApples;
		return this;
	}
	
	public Pipeline3 findLowFatApples() {
		List<Apple> lowFatRedApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if(apple.getCalory() < 100){
				lowFatRedApples.add(apple);
			}
		}
		apples = lowFatRedApples;
		return this;
	}
	
	public List<Apple> getApples(){return apples;}
	
	public static void main(String[] args) {
		new Pipeline3()
				.findLowFatApples()
				.findRedApples()
				.getApples();
	}
}

Builder / Decoration Pattern

Describe the Stream interface and pipelines

public class Pipeline4{
	...
	
	public Pipeline4 filter(Predicate<Apple> p) {
		List<Apple> redApples = new ArrayList<Apple>();
		for(Apple apple : apples){
			if(p.test(apple)){
				redApples.add(apple);
			}
		}
		apples = redApples;
		return this;
	}
	
	public List<Apple> getApples(){return apples;}
	
	public static void main(String[] args) {
		new Pipeline4()
		    .filter(apple -> "red".equals(apple))
                    .filter(apple -> apple.getCalory() < 100)
		    .getApples();
	}
}

What else do you see here?

Filter a collection using lambda expression

http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

Stream Interface

public class Pipeline5{
	...

	public List<Apple> findLowFatAndRedApples(){
		return apples.stream()
				.filter(apple -> "red".equals(apple))
				.filter(apple -> apple.getCalory() < 100)
				.collect(Collectors.toList());
	}
	
	public static void main(String[] args) {
		new Pipeline5().findLowFatAndRedApples();
	}
}

Identify lambda operation that are lazy

Operation returns Stream => Lazy

Stream<Apple> s = sapples.stream()
                            .filter(apple -> {
                                System.out.println("Log");
                                return "red".equals(apple)
                            })  // lazy
List<Apple> redApples = apples.stream()
                            .filter(apple -> {
                                System.out.println("Log");
                                return "red".equals(apple)
                            })
                            .collect(Collectors.toList()); // eager

Operation returns something or void => Eager

Reference

  • http://docs.oracle.com/javase/tutorial/collections/streams/
  • http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
  • http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
  • http://docs.oracle.com/javase/tutorial/collections/streams/#pipelines
  • http://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#laziness

Collection Operation with Lambda

  • Using map
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
  • Describe the unique characteristics of the Optional 
  • Using count, max, min, average, sum
  • Sort a collection using lambda expression
  • Using collect method/Collector, averagingDouble, groupingBy, joining, partitioningBy

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using map
public class MapEx extends BaseTest{

	@Override
	public Object withJava() {
		List<String> names = new ArrayList<String>();
		for(Player p : players){
			names.add(p.getName());
		}
		return names;
	}

	@Override
	public void withJava8(Object expectedResult) {
		List<String> names = players.stream()
				.map(Player::getName)
				.collect(Collectors.toList());
		assertEquals(expectedResult, names);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
public class FindAnyEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Integer> ages = new ArrayList<Integer>();
		for(Player p : players){
			if(p.getAge() == 30)
				ages.add(p.getAge());
		}
		Collections.shuffle(ages);
		return ages.get(0);
	}

	@Override
	public void withJava8(Object expectedResult) {
		Optional<Player> player = players.stream()
				.filter(p -> p.getAge() == 30)
				.findAny();
		assertEquals(expectedResult, player.get().getAge());
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
public class FindFirstEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Integer> ages = new ArrayList<Integer>();
		for(Player p : players){
			if(p.getAge() == 30)
				ages.add(p.getAge());
		}
		return ages.get(0);
	}

	@Override
	public void withJava8(Object expectedResult) {
		Optional<Player> player = players.stream()
				.filter(p -> p.getAge() == 30)
				.findFirst();
		assertEquals(expectedResult, player.get().getAge());
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
public class AllMatchEx extends BaseTest{

	@Override
	public Object withJava() {
		for(Player p : players){
			if(p.getAge() != 30)
				return false;
		}
		return true;
	}

	@Override
	public void withJava8(Object expectedResult) {
		Boolean b = players.stream()
				.allMatch(p -> p.getAge() == 30);
		assertEquals(expectedResult, b);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
public class NoneMatchEx extends BaseTest{

	@Override
	public Object withJava() {
		for(Player p : players){
			if(p.getAge() == 30)
				return false;
		}
		return true;
	}

	@Override
	public void withJava8(Object expectedResult) {
		Boolean b = players.stream()
				.noneMatch(p -> p.getAge() == 30);
		assertEquals(expectedResult, b);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using findFirst, findAny, anyMatch, allMatch, noneMatch
public class AnyMatchEx extends BaseTest{

	@Override
	public Object withJava() {
		for(Player p : players){
			if(p.getAge() == 30)
				return true;
		}
		return false;
	}

	@Override
	public void withJava8(Object expectedResult) {
		Boolean b = players.stream()
				.anyMatch(p -> p.getAge() == 30);
		assertEquals(expectedResult, b);
	}
}

Describe the unique characteristics of the Optional 

http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using count, max, min, average, sum
public class CountEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Player> ps = new ArrayList<Player>();
		for(Player p : players){
			if(p.getAge() == 30)
				ps.add(p);
		}
		return ps.size();
	}

	@Override
	public void withJava8(Object expectedResult) {
		long c = players.stream()
				.filter(p -> p.getAge() == 30)
				.count();
		assertEquals(expectedResult, (int)c);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using count, max, min, average, sum
public class AverageEx extends BaseTest{

	@Override
	public Object withJava() {
		int sum = 0;
		int count = 0;
		for(Player p : players){
			if(p.getAge() == 30){
				sum += p.getAge();
				count++;
			}
		}
		return (double) sum / count; // return sum
	}

	@Override
	public void withJava8(Object expectedResult) {
		OptionalDouble average = players.stream()
				.filter(p -> p.getAge() == 30)
				.mapToInt(p -> p.getAge())
				.average(); // sum()
				
		assertEquals(expectedResult, average.getAsDouble());
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Sort a collection using lambda expression
public class SortEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Player> ps = players;
		Collections.sort(players, new Comparator<Player>() {

			@Override
			public int compare(Player p1, Player p2) {
				return p1.getName().compareTo(p2.getName());
			}
		});
		return ps;
	}

	@Override
	public void withJava8(Object expectedResult) {
		List<Player> ps = players;
		ps.sort((p1, p2) ->  p1.getName().compareTo(p2.getName()));
				
		assertEquals(expectedResult, ps);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using collect method/Collector, averagingDouble, groupingBy, joining, partitioningBy
public class AverageEx2 extends BaseTest{

	@Override
	public Object withJava() {
		int sum = 0;
		int count = 0;
		for(Player p : players){
			if(p.getAge() == 30){
				sum += p.getAge();
				count++;
			}
		}
		return (double) sum / count;
	}

	@Override
	public void withJava8(Object expectedResult) {
		Double average = players.stream()
				.filter(p -> p.getAge() == 30)
				.collect(Collectors.averagingDouble(p -> (double)p.getAge()));
				
		assertEquals(expectedResult, average);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using collect method/Collector, averagingDouble, groupingBy, joining, partitioningBy
public class GroupingByEx extends BaseTest{

	@Override
	public Object withJava() {
		Map<String, List<Player>> ps = new HashMap<String, List<Player>>();
		for(Player p : players){
			String key = p.getNationality();
			if(ps.containsKey(key)){
				ps.get(key).add(p);
			}else{
				List<Player> value = new ArrayList<Player>();
				value.add(p);
				ps.put(key, value);
			}
		}
		return ps; 
	}

	@Override
	public void withJava8(Object expectedResult) {
		Map<String, List<Player>> ps = players.stream()
				.collect(Collectors.groupingBy(p -> p.getNationality()));
				
		assertEquals(expectedResult, ps);
	}
}

Collection Operation with Lambda

  • https://github.com/ajoshow/ocjp8ex.git
  • Using collect method/Collector, averagingDouble, groupingBy, joining, partitioningBy
public class PartitioningByEx extends BaseTest{

	@Override
	public Object withJava() {
		Map<Boolean, List<Player>> ps = new HashMap<Boolean, List<Player>>();
		ps.put(true, new ArrayList<Player>());
		ps.put(false, new ArrayList<Player>());
		for(Player p : players){
			if(p.getAge() > 28){
				ps.get(true).add(p);
			}else{
				ps.get(false).add(p);
			}
		}
		return ps; 
	}

	@Override
	public void withJava8(Object expectedResult) {
		Map<Boolean, List<Player>> ps = players.stream()
				.collect(Collectors.partitioningBy(p -> p.getAge() > 28));
				
		assertEquals(expectedResult, ps);
	}
}

Collection Operation with Lambda

  • Using collect method/Collector, averagingDouble, groupingBy, joining, partitioningBy
public class JoiningEx extends BaseTest{

	@Override
	public Object withJava() {
		StringBuilder sb = new StringBuilder();
		int count = 0;
		for(Player p : players){
			if(count == 0){
				sb.append(p.getName());
				count++;
			}else{
				sb.append(", ").append(p.getName());
			}
		}
		return sb.toString(); 
	}

	@Override
	public void withJava8(Object expectedResult) {
		String name = players.stream()
		 		.map(p -> p.getName())
				.collect(Collectors.joining(", "));
				
		assertEquals(expectedResult, name);
	}
}

Using Collector

class MyCollector extends Collector<Person, Container, Integer>

Input

Output

Accumulator

methods to be implemented 

supplier() -> create / initial container

accumulator() -> accumulate input to container

combiner() -> combine accumulators ( for parallel computing)

finisher() -> finalize the type of result 

could be any sort of builder class... ex: StringBuilder, to keep track the accumulated result.

Using Collector

class MyCollector implements Collector<Person, Container, Integer>{

	@Override
	public Set<java.util.stream.Collector.Characteristics> characteristics() {
		return EnumSet.of(Characteristics.UNORDERED);
	}

	@Override
	public Supplier<Container> supplier() {
		return () -> new Container();
	}

	@Override
	public BiConsumer<Container, Person> accumulator() {
		return (c, p) -> c.add(p.getAge());
	}

	@Override
	public BinaryOperator<Container> combiner() {
		return (left, right) -> left.merge(right);
	}

	@Override
	public Function<Container, Integer> finisher() {
		return (c) -> c.get();
	}
}
class Container{
	private Integer n = 0;
	public void add(Integer n){
		this.n += n;
	}
	
	public void add(Person p){
		this.n += p.getAge();
	}
	
	public Integer get(){
		return n;
	}
	
	public Container merge(Container c){
		n += c.get();
		return this;
	}
}

To calculate sum of all player's ages.

Using Collector

class MyCollector2 implements Collector<Person, Container, Integer>{

	@Override
	public Set<java.util.stream.Collector.Characteristics> characteristics() {
		return EnumSet.of(Characteristics.UNORDERED);
	}

	@Override
	public Supplier<Container> supplier() {
		return () -> new Container();
	}

	@Override
	public BiConsumer<Container, Person> accumulator() {
		return Container::add;
	}

	@Override
	public BinaryOperator<Container> combiner() {
		return Container::merge;
	}

	@Override
	public Function<Container, Integer> finisher() {
		return Container::get;
	}
	
}
Integer result = players.stream().collect(new MyCollector());
Integer result2 = players.stream().collect(new MyCollector2());

Using Collector method / Reduction

Integer total = players.stream()
		.reduce(
                    0, 
		    (i, p) -> i += p.getAge(), 
		    (left, right) -> left + right
		);
Integer total = players.stream()
			.collect(Collectors.reducing
                                    (
				        0,
					(p) -> p.getAge(),
					(left, right) -> left + right
				    )
                                );

We get the same result by using reduction methods. by reduce (from Stream) or reducing (from Collectors)

Reference

  • http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
  • http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
  • http://www.oracle.com/technetwork/articles/java/architect-lambdas-part2-2081439.html

Parallel Stream

  • Develope/uses parallel streams
  • Implement decomposition, reduction, in streams

Develope/uses parallel streams

public static void main(String... args) {

    long sum = IntStream.range(1, 100_000).parallel().sum();
    System.out.println("Sum: " + sum);
		
    int x = Arrays.asList(1,2,3,4,5,6,7,8,9,10)
	.parallelStream()
	.collect(Collectors.summingInt(i -> i));
    System.out.println(x);
}

be brief:

change from stream() to parallelStream()

 

Stream -> parallel()

Collection -> paraelStream()

Develope/uses parallel streams

  • fork/join parallel computing (provided from Java7)
  • Parallel Stream
  • Concurrent and Unsorted Collector Characteristics 
  • Avoid interfering
  • Avoid stateful lambda expression
  • Consideration of collection size, memory, performance

Implement decomposition, reduction, in streams

public class ReduceParallelStreamEx {
	public static void main(String... args) {

		List<Person> persons = Stream.generate(
                                    ()->new Person( (int)(Math.random()*99)+1 )
                                )
		                .limit(10)
				.collect(Collectors.toList());
								
		int x = persons.parallelStream()
				.mapToInt(p -> p.getAge())
				.reduce(0, (a,b) -> b > a ? b : a);
		System.out.println("Max: " + x);
	}
	
	static class Person{
		private int age;
		Person(int age){
			this.age = age;
		}
		public int getAge(){
			return age;
		}
	}
}

Reference

  • https://weblogs.java.net/blog/kocko/archive/2014/12/19/java8-how-implement-custom-collector
  • http://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html
  • http://docs.oracle.com/javase/tutorial/collections/streams/reduction.html
  • https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#spliterator--
  • https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps

Lambda Cookbook

  • Using Java8 collection improvements
  • Read files using lambda improvements
  • Use merge, flatMap methods on a collection
  • Describe Arrays.stream(), IntStream.range()

Using Java8 collection improvements

  • Collection.removeIf
  • List.replaceAll
  • Map.computeIfAbsent
  • Map.computerIfPresent
  • Map.forEach

Using Java8 collection improvements

Collection.removeIf

public class RemoveIfEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Player> ps = getPlayers();
		Iterator<Player> iterator = ps.iterator();
		while(iterator.hasNext()){
			if(iterator.next().getAge() > 30){
				iterator.remove();
			}
		}
		return ps;
	}

	@Override
	public void withJava8(Object expectedResult) {
		List<Player> ps = getPlayers();
		ps.removeIf(p -> p.getAge() > 30);
		assertEquals(expectedResult, ps);
	}
}

Using Java8 collection improvements

List.replaceAll

public class ReplaceAllEx extends BaseTest{

	@Override
	public Object withJava() {
		List<Player> ps = getPlayers();
		for(int i=0; i<ps.size(); i++){
			ps.set(i, null);
		}
		return ps;
	}

	@Override
	public void withJava8(Object expectedResult) {
		List<Player> ps = getPlayers();
		ps.replaceAll(p -> null);
		assertEquals(expectedResult, ps);
	}
}

Using Java8 collection improvements

  • Map.forEach
public class MapForEachEx extends BaseTest{
	Map<Integer, String> map;
	
	@Override
	public void init(){
		map = new HashMap<Integer, String>();
		map.put(1,"a");
		map.put(2,"b");
		map.put(3,"c");
	}
	
	@Override
	public Object withJava() {
		Iterator<Entry<Integer, String>> iterator = map.entrySet().iterator();
		while(iterator.hasNext()){
			Entry<Integer, String> entry = iterator.next();
			System.out.println("Key:" + entry.getKey() + " Value: " + entry.getValue());
		}
		return map;
	}

	@Override
	public void withJava8(Object expectedResult) {
		map.forEach((k,v) -> System.out.println("Key:" + k + " Value: " + v));
		assertEquals(expectedResult, map);
	}
}

Using Java8 collection improvements

  • Map.computeIfAbsent
  • Map.computerIfPresent
public class MapComputeIfEx extends BaseTest{
	Map<Integer, String> map;
	
	@Override
	public void init(){
		map = new HashMap<Integer, String>();
		map.put(1,"a");
		map.put(2,"b");
		map.put(3,"c");
	}
	
	@Override
	public Object withJava() {
		String s = map.get(1);
		String s2 = map.get(4);
		String s3 = map.get(1);
		if(s == null){ s = "d"; }
		if(s2 == null){ s2 = "e"; }
		if(s3 != null){ s3 = "f"; }
		System.out.println(s + s2 + s3);
		return null;
	}

	@Override
	public void withJava8(Object expectedResult) {
		String s = map.computeIfAbsent(1, k -> "d");
		String s2 = map.computeIfAbsent(4, k -> "e");
		String s3 = map.computeIfPresent(1, (k, v) -> "f");
		System.out.println(s + s2 + s3);
	}
}

Read files using lambda improvements

public class FileImproveEx {

	public static void main(String... args) throws IOException {

		Path start = new File(".").toPath();
		BiPredicate<Path, BasicFileAttributes> topicDirectoriesMatcher = (path,
				basicFileAttributes) -> basicFileAttributes.isDirectory()
				&& path.toString().contains("topic6");

		System.out.println("Found directories:");
		Stream<Path> pathStream = Files.find(start, Integer.MAX_VALUE,
				topicDirectoriesMatcher);
		pathStream.forEach(System.out::println);

		List<Path> paths = Files.walk(start)
				.filter(p -> p.toString().contains("FileImproveEx.java"))
				.collect(Collectors.toList());
		System.out.println("\nFile path: " + paths.get(0));

		System.out.println("\nFile content: ");
		Files.lines(paths.get(0), Charset.defaultCharset()).forEach(
				System.out::println);

	}
}

Files.find => find files from the given starting file path

Files.walk => traversal the file tree from the given starting file path

Files.lines => traversal line from the given file path

Use merge, flatMap methods on a collection

public class FlatMapEx extends BaseTest{

	@Override
	public Object withJava() {
		List<String> teamNames = new ArrayList<String>();
		for(Tournament t : tournaments){
			List<Team> teams = t.getTeams();
			if(teams != null){
				for(Team team : teams){
					teamNames.add(team.getName());
				}
			}
		}
		return teamNames;
	}

	@Override
	public void withJava8(Object expectedResult) {
		List<String> teamNames = tournaments.stream()
					.flatMap(t -> t.getTeams().stream())
					.map(team -> team.getName())
					.collect(Collectors.toList());
		assertEquals(expectedResult, teamNames);
	}
}

FlatMap

Use merge, flatMap methods on a collection

public class MergeEx extends BaseTest{
	Map<Integer, String> map;
	
	@Override
	public void init(){
		map = new HashMap<Integer, String>();
		map.put(1,"a");
		map.put(2,"a");
	}
	
	@Override
	public Object withJava() {
		String s = map.get(1);
		map.put(1, "hello " + s);
		return map.get(1);
	}

	@Override
	public void withJava8(Object expectedResult) {
		String newValue = map.merge(2, "hello", (s1, s2) -> s2 + " " + s1);
		assertEquals(expectedResult, newValue);
	}
}

Merge

Describe Arrays.stream(), IntStream.range()

public class IntStreamEx {

	public static void main(String... args) throws IOException {

		int[] nums = IntStream.rangeClosed(1, 3).toArray();
		
		IntStream is = Arrays.stream(nums);
		is.forEach(System.out::println);
		
		IntStream.rangeClosed(1, 3)
				.forEach(System.out::println);;
				
	}
}
  • primitive type, 
  • no boxing concern 
  • better performance

Reference

  • http://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#find-java.nio.file.Path-int-java.util.function.BiPredicate-java.nio.file.FileVisitOption...-
  • http://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#range-int-int-
  • http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#flatMap-java.util.function.Function-

Method Enhancements

  • Define and use a default method of a interface, describe the inheritance rules for a default method
  • Adding static methods to interface

Define and use a default method of a interface, describe the inheritance rules for a default method

interface Foo{ void noo(); }
interface Boo{ void noo(); }
interface Moo{ void noo(); }
class Hoo implements Foo, Boo, Moo{
	@Override
	public void noo() {}
}
interface Foo2{ default void noo(){System.out.println("foo");}; }
interface Boo2{ void noo(); }
class Hoo2 implements Foo2, Boo2{
	@Override
	public void noo() {System.out.println("hoo");}
}
abstract class Foo4{ abstract void noo(); }
interface Boo4{ default void noo(){System.out.println("boo");}; }
class Hoo4 extends Foo4 implements Boo4{
	@Override
	public void noo() {System.out.println("hoo");}
}
abstract class Foo3{ abstract void noo(); }
interface Boo3{ void noo(); }
class Hoo3 extends Foo3 implements Boo3{
	@Override
	public void noo() {System.out.println("hoo");}
}
abstract class Foo5{ abstract void noo(); }
interface Boo5{ default void noo(){System.out.println("boo");}; }
interface Moo5{ void noo(); }
class Hoo5 extends Foo5 implements Boo5, Moo5{
	@Override
	public void noo() {System.out.println("hoo");}
}
interface Boo7{ default void noo(){System.out.println("boo");} }
interface Moo7{ default void noo(){System.out.println("moo");} }
class Hoo7 implements Boo7, Moo7{
	@Override
	public void noo() {System.out.println("hoo");}
}
abstract class Foo6{ abstract void noo(); }
interface Boo6{ default void noo(){System.out.println("boo");} }
interface Moo6{ default void noo(){System.out.println("moo");} }
class Hoo6 extends Foo6 implements Boo6, Moo6{
	@Override
	public void noo() {System.out.println("hoo");}
}
public static void main(String[] args){
	MutiInterfaceEx obj = new MutiInterfaceEx();
	obj.new Hoo().noo(); obj.new Hoo2().noo();
	obj.new Hoo3().noo(); obj.new Hoo4().noo();
	obj.new Hoo5().noo(); obj.new Hoo6().noo();
	obj.new Hoo7().noo();
}

point out: 1) compiler error, 2) runtime error, 3) what's output 

1

2

3

4

5

6

7

Define and use a default method of a interface, describe the inheritance rules for a default method

public class DefaultInheritEx {
	
	interface Parent{ 
		default void hi(){System.out.println("parent hi");} 
	}
	interface Child{ 
		default void hi(){System.out.println("child hi");}; 
	}
	interface Child2 extends Parent{ 
		default void hi(){System.out.println("child2 hi");}; 
	}
	
	class ParentImpl implements Parent{
		@Override public void hi(){}; 
	}
	class ChildImpl extends ParentImpl implements Child{
		@Override public void hi(){System.out.println("child impl hi");}
	}
	class ChildImpl2 extends ParentImpl implements Child2{
		@Override public void hi(){System.out.println("child impl2 hi");} 
	}
	class ChildImpl3 implements Child{
	}
	class ChildImpl4 implements Child, Child2{
	}
	class ChildImpl5 extends ParentImpl implements Child, Child2{
	}
	
	public static void main(String[] args){
		DefaultInheritEx obj = new DefaultInheritEx();
		obj.new ChildImpl().hi();
		obj.new ChildImpl2().hi();
		obj.new ChildImpl3().hi();
		obj.new ChildImpl4().hi();
		obj.new ChildImpl5().hi();
	}
	
}

Any Error?

1) Comiple error

2) Runtime error

 

If no error, then what's the possible output ?

Define and use a default method of a interface, describe the inheritance rules for a default method

Rule 1:

classes always win. A method declaration in the class or a superclass takes priority over any default method declaration.

 

Rule2:

otherwise, sub-interface win. the method win the same signature in the most specific default-providing interface is selected.

 

Rule3:

if the choice is still ambiguous, the class inheriting from multiple interfaces has to explicitly select which default method implementation to use by overriding it and calling the desired method explicitly.

 

 

(reference: book Java 8 in Action.) 

Define and use a default method of a interface, describe the inheritance rules for a default method

Example of Rule1: 

interface A has a default method.

class B extends class A

(use default method from A) 

 

Example of Rule2: class

interface B extends A, whereas the interface A has a default method

class C implements B, A

(use default method from B)

 

Example of Rule3:

B and A have the default method with the same signature

class C implements B, A

C explicitly overrides the default method.

(use default method from C)

Adding static methods to interface

Static methods are similar to default methods except that we can’t override them in the implementation classes

public class StaticDefaultEx {
	
	interface Parent{ 
		static void hi(){
			// must have body
		}
	}
	class Child implements Parent{
		static void hi(){
			// complier error
		}
	}
	class Child2 implements Parent{
		void hi(){
			// ok, this is a class method
		}
	}
}

Reference

  • http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

Use Java8 Date/Time API

  • Create and manage date-based and time-based events; including combination of date and time into a single object using  LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration
  • Work with dates and times across time-zones and manage changes resulting from daylight savings
  • Define and create timestamps, periods and durations; apply formatting to local and zoned dates and times

Use Java8 Date/Time API

Why we need new Date/Time API

  • Immutable-value classes, Thread-Safe 
  • Domain driven design, less confusion date time representation
  • Separation of chronologies

 

Create and manage date-based and time-based events; including combination of date and time into a single object using  LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration

public class DateTimeEx {

	public static void main(String... args) throws IOException {
	    /* LocalDate */
	    LocalDate specificDate = LocalDate.of(2015, 10, 26);
	    LocalDate todayDate = LocalDate.now();
	    
	    /* LocalTime */
	    LocalTime specificTime = LocalTime.of(23, 59, 59);
	    LocalTime nowTime = LocalTime.now();

	    /* LocalDateTime */
	    System.out.println(LocalDate.parse("2015-10-26"));
	    System.out.println(LocalTime.parse("23:59:59"));
	    LocalDateTime specificDateTime = LocalDateTime.of(1985, Month.OCTOBER, 26, 23, 59, 29);
	    LocalDateTime nowDateTime = LocalDateTime.now();
	   
	    /* Instant */
	    // 1 Million seconds since the Unix epoch time
	    Instant oneMSInstant = Instant.ofEpochSecond(1_000_000_000); 
	    Instant nowInstant = Instant.now(); // seconds since the Unix epoch time to now

	    /* Duration */  // A date-based amount of time 
	    Duration.between(specificTime, nowTime);

	    /* Period */  // A date-based amount of time 
	    Period.between(specificDate, todayDate);
				
	}
}

Work with dates and times across time-zones and manage changes resulting from daylight savings

public class TimeZoneEx {

	public static void main(String... args) throws IOException {
		LocalDateTime now = LocalDateTime.now();

	    ZoneId zid1 = ZoneId.of("Asia/Tokyo");
	    ZonedDateTime t1 = ZonedDateTime.of(now, zid1);
	    System.out.println("Time at Tokyo zone now: " + t1);

	    ZoneId zid2 = ZoneId.of("America/Los_Angeles");
	    ZonedDateTime t2 = t1.withZoneSameInstant(zid2);
	    System.out.println("Same time at LA zone: " + t2);
	    
	    Duration daySaving = zid2.getRules().getDaylightSavings(Instant.now());
	    System.out.println("LA zone day light saving offset: " + daySaving);
	    
	    System.out.println("Same time at LA zone with day light saving: " + t2.minusSeconds(daySaving.getSeconds()));
				
	}
}

Define and create timestamps, periods and durations; apply formatting to local and zoned dates and times

public class DateTimeFormatEx {

	public static void main(String... args) throws IOException {
		LocalDateTime now = LocalDateTime.now();
		System.out.println(now.format(DateTimeFormatter.ISO_DATE));
		System.out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
		System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss")));
		System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss",Locale.TAIWAN)));
	}
}

Reference

  • http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
  • http://www.codedata.com.tw/java/jodatime-jsr310-2-time-abc/#.VRZ5QEHWADg.facebook

JavaScript on Java with Nashorn

  • Develop Javascript code that creates and uses Java members such as Java objects, methods, JavaBeans, Arrays, Collections, Interfaces.
  • Develop code that  Evaluates JavaScript in java, Passes Java object to Javascript, inovkes Javascript function and call methods on javascript objects.

JavaScript on Java with Nashorn

  • The Nashorn Javascript Engine is part of Java SE 8 and competes with other standalone engines like Google V8 (the engine that powers Google Chrome and Node.js). Nashorn extends Javas capabilities by running dynamic javascript code natively on the JVM
  • Just JavaScript - jjs, located at $Java_Home/bin

JavaScript on Java with Nashorn

Evaluated by passing javascript code

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('Hello World!');");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("script.js"));

Evaluated by passing javascript file

Invoking Javascript Functions from Java

// script.js
var greeting = function(name) {
    print('Hello, ' + name);
    return "Hello from javascript";
};
// MyJavaClass.java
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("script.js"));

Invocable invocable = (Invocable) engine;

Object result = invocable.invokeFunction("greeting", "Peter");
System.out.println(result);

// output:
// hello, Peter
// Hello from javascript

Develop Javascript code that creates and uses Java members such as Java objects, methods, JavaBeans, Arrays, Collections, Interfaces.

// script.js
var print= function (object) {
    print("JS Class Definition: " + Object.prototype.toString.call(object));
};
// MyJavaClass.java
invocable.invokeFunction("print", new Date());
// [object java.util.Date]

invocable.invokeFunction("print", LocalDateTime.now());
// [object java.time.LocalDateTime]

invocable.invokeFunction("print", new Person());
// [object com.winterbe.java8.Person]

Invoking Java Methods from Javascript

// MyJavaClass.java
static String greeting(String name) {
    System.out.format("Hello, %s", name);
    return "Hello from java";
}
// whatever.js
var MyJavaClass = Java.type('my.package.MyJavaClass');

var result = MyJavaClass.greeting('Juke');
print(result);

// output:
// Hello, Juke
// Hellow from java

ScriptObjectMirror

// foo.js
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.getFullName = function() {
        return this.firstName + " " + this.lastName;
    }
}
// MyJavaClass.java
static void fullname(ScriptObjectMirror person) {
    System.out.println("Full Name is: " + person.callMember("getFullName"));
}

var p = new Person("Peter", "Parker");
MyJavaClass.fullname(p); // Full Name is: Peter Parker

The javascript method getFullName can be called on the ScriptObjectMirror via callMember()

Language Extension

// whatever.js
var ArrayList = Java.type('java.util.ArrayList');
var list = new ArrayList();
list.add('a');
list.add('b');
list.add('c');

for each (var el in list) print(el);  // a, b, c

Collection and For Each

// whatever.js
var map = new java.util.HashMap();
map.put('foo', 'val1');
map.put('bar', 'val2');

for each (var e in map.keySet()) print(e);  // foo, bar

for each (var e in map.values()) print(e);  // val1, val2

HashMap

Language Extension

Lambda

var list = new java.util.ArrayList();
list.add("aaa2");
// add more el

list
    .stream()
    .filter(function(el) {
        return el.startsWith("aaa");
    })
    .sorted()
    .forEach(function(el) {
        print(el);
    });

Language Extension

Extending classes

// whatever.js
var Runnable = Java.type('java.lang.Runnable');
var Printer = Java.extend(Runnable, {
    run: function() {
        print('printed from a separate thread');
    }
});

var Thread = Java.type('java.lang.Thread');
new Thread(new Printer()).start();

new Thread(function() {
    print('printed from another thread');
}).start();

// printed from a separate thread
// printed from another thread

Reference

  • http://winterbe.com/posts/2014/04/05/java8-nashorn-tutorial/
  • ​http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html

Reference

Sample Code at Github

https://github.com/ajoshow/ocjp8ex

Learning Java 8, Lambda

By Andy Chu