Unlike Java 7, Java 8 has some significant changes. You can get familiarised with the the following simple working code. All you need is Java 8 installed on your machine.
#1: Interface can have static and default methods. This tries to solve the diamond (aka multiple inheriance) issue. In the past it was essentially impossible for Java libraries to add methods to interfaces. Adding a method to an interface would mean breaking all existing code that implements the interface. Now, as long as a sensible default implementation of a method can be provided, library maintainers can add methods to these interfaces. The static methods serve as utility methods, hence you don’t need a separate helper class.
#2: Lambda expression for supporting closures, which simplifies your code. In the past, you need to write anonymous classes. Lambda expressions are anonymous methods which are intended to replace the bulkiness of anonymous inner classes with a much more compact mechanism.
Here is a basic example that covers #1 and #2. A simple button click example.
Let’s define an interface with a static method (e.g. a utility function) and two default methods as shown below.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.java8.examples; import java.awt.event.ActionEvent; public interface Test { //Java 8: static method in an interface static void doSomething(ActionEvent event) { System.out.println("B1 clicked ....."); } default void doAnotherSomething(ActionEvent event) { System.out.println("B2 clicked ....."); } //Java 8: default method in interface default void doWork(){ System.out.println("executing default method"); } } |
Define the TestImpl class, which implements the interface Test. The TestImpl uses closures via lambda expressions and makes use of the methods implemented in the interface Test.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.java8.examples; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.Button; import javax.swing.JFrame; public class TestImpl implements Test { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setLayout(new FlowLayout()); Button button1 = new Button("B1"); Button button2 = new Button("B2"); Button button3 = new Button("B3"); Button button4 = new Button("B4"); frame.getContentPane().add(button1); frame.getContentPane().add(button2); frame.getContentPane().add(button3); frame.getContentPane().add(button4); //Java 8: Lambda expressions & invocation of concrete methods in interface //invokes static method on the interface button1.addActionListener(Test::doSomething); //static method reference Test testObj = new TestImpl(); button2.addActionListener(testObj::doAnotherSomething); //instance method reference //input is e and result is println(..) and doWork() functional interface method invocation button3.addActionListener((e) -> {System.out.println("B3 clicked ..."); testObj.doWork(); }); //old way: pre Java 8: Anonymous inner class button4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("B4 clicked"); } }); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } |
If you click on all 4 buttons from B1 to B4, you will get an output of
|
1 2 3 4 5 6 |
B1 clicked ..... B2 clicked ..... B3 clicked ... executing default method B4 clicked |
Q. How does ” button3.addActionListener((e) -> {System.out.println(“B3 clicked …”); testObj.doWork(); }); ” know what method to execute? Did you notice that there is no new ActionListener( ) and no public void actionPerformed(ActionEvent event)?
A. Here the compiler understands that there is only one method in the ActionListener interface and that takes only one parameter. So obviously, it decides (finalizes) that the e as a reference to ActionEvent.
The functional interfaces that are annotated with @FunctionalInterface in Java 8 ensures that you can only define one abstract method. You can define as many default and static method implementations as you want.
#3 Java 8 adopts Joda as its native date & time framework. Due to what was perceived as a design flaw in Joda, Java 8 implemented its own new date / time API from scratch. The new APIs were designed with simplicity in mind.
All the core classes in the new API are constructed by fluent factory methods. When constructing a value by its constituent fields, the factory is called of; when converting from another type, the factory is called from.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.java8.examples; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; public class TimeTest { public static void main(String[] args) { // The current date and Time LocalDateTime timePoint = LocalDateTime.now(); System.out.println("now=" + timePoint); //10th of Jan 2012 LocalDate pastDate = LocalDate.of(2012, Month.JANUARY, 10); System.out.println("pastDate=" + pastDate); LocalTime mathsClassTonight = LocalTime.of(19, 30); System.out.println("mathsClassTonight=" + mathsClassTonight); mathsClassTonight = LocalTime.parse("19:30:50"); // From a String System.out.println("mathsClassTonight=" + mathsClassTonight); //add 12 days to pastDate LocalDate plusDays = pastDate.plusDays(12); System.out.println("pastPlus12days=" + plusDays); LocalDate epochDay = LocalDate.ofEpochDay(150); // 150 days from 01/01/1970 System.out.println("epochDay=" + epochDay); } } |
The output will be:
|
1 2 3 4 5 6 7 |
now=2014-04-02T16:27:41.467 pastDate=2012-01-10 mathsClassTonight=19:30 mathsClassTonight=19:30:50 pastPlus12days=2012-01-22 epochDay=1970-05-31 |
Example 1: The java.lang.Runnable interface has been made a functional interface.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.java8.examples; public class Example1 { public static void main(String[] args) { System.out.println("main thread: " + Thread.currentThread().getName()); //run method takes no arguments so: () -> (body) //closure worker thread1 Runnable r = () -> (System.out.println("worker thread: " + Thread.currentThread().getName())); new Thread(r).start(); //pass the Runnable closure to Thread constructor //closure worker thread2 new Thread( () -> (System.out.println("worker thread: " + Thread.currentThread().getName()))).start(); } } |
Output:
main thread: main
worker thread: Thread-0
worker thread: Thread-1
Example 2: The java.util.function package has a number of functional interfaces.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.java8.examples; import java.util.function.Consumer; public class Example2 { public static void main(String[] args) { //closure of Consumer 1. the accept(T t) method takes Integer here Consumer<Integer> c1 = (x) -> {System.out.println("consumed: " + x);}; //closure of Consumer 2. x is an Integer Consumer<Integer> c2 = (x) -> {System.out.println("consumed: " + x*x);}; c1.accept(5); c2.accept(5); //andThen is a default method implementation in Consumer interface Consumer<Integer> c3 = c1.andThen(c2); c3.accept(6); } } |
Output:
consumed: 5
consumed: 25
consumed: 6
consumed: 36
Example 3: The Function
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.java8.examples; import java.util.function.Function; public class Example3 { public static void main(String[] args) { //closure of Function. apply(T t) method takes Double here Function<Double, Double> c1 = (arg) -> (arg + arg); Function<Double, Double> c2 = (arg) -> (arg * arg); Double result1 = c1.apply(5.0); Double result2 = c2.apply(5.0); System.out.println("result1= " + result1); System.out.println("result2= " + result2); //andThen is a default method implementation in Function interface Function<Double, Double> c3 = c1.andThen(c2); Double result3 = c3.apply(6.0); System.out.println("result3= " + result3);// 6 + 6 = 12, 12 * 12 = 144; } } |
Output:
result1= 10.0
result2= 25.0
result3= 144.0
Example 4: The Predicate
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.java8.examples; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class Example4 { private static final int SHORT_NAME_LENGTH = 3; public static void main(String[] args) { List<String> languages = Arrays.asList("Java", "C++", "PHP", "C#", "Scala", "JRuby", "Jython"); //The Predicate interface has test(T t) that returns a boolean Predicate<String> shortNamePredicate = (s) -> (s.length() <= SHORT_NAME_LENGTH); // closure Predicate<String> startsWithCPredicate = (s) -> (s.startsWith("J")); //closure languages.stream() .filter(shortNamePredicate) .forEach(e -> System.out.println("short names: " + e)); languages.stream() .filter(startsWithCPredicate) .forEach(e -> System.out.println("starts with J: " + e)); } } |
Output:
short names: C++
short names: PHP
short names: C#
starts with J: Java
starts with J: JRuby
starts with J: Jython
