Java 8 API Tutorial
In this article, we are going to explain the Java 8 API through examples.
1. Introduction
Java 8 was released on March 18, 2014 with several enhancements. In this example, I will demonstrate the following API enhancements:
- Support Functional programming with Lambda Expression, Functional Interface, and Stream API
- New Java Date API
- Interface’s Default Method
2. Technologies Used
The example code in this article was built and run using:
- Java 8
- Maven 3.3.9
- Eclipse Oxygen
- Junit 4.12
3. Maven Project
3.1 Dependencies
I will include Junit
in the pom.xml
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>zheng.jcg.demo</groupId> <artifactId>java8-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
4. Functional Programming
Java 8 supports functional programming with a lambda expression, functional interfaces, and Stream API. Stream API provides methods which take functional interface as an argument. The filter
method takes a Predicate
argument. The map
method takes a Function
argument. etc.
4.1 Functional Interface
A functional interface is an interface with only one abstract method. Java 8 provides @FunctionalInterface
annotation that marks the interface as a functional interface. In this step, I will create a GreetingFunction
interface which is annotated with @FunctionalInterface
. It has three methods:
speak()
– this is an abstract method and can be implemented with a lambda expression.hello()
– this is a default method.bye()
– this is another default method.
GreetingFunction.java
package com.zheng.demo; @FunctionalInterface public interface GreetingFunction { void speak(String message); default void hello() { System.out.println("Hello!"); } default void bye() { System.out.println("Bye!"); } }
Note: line 3: marks this interface with a @FunctionalInterface annotation.
Click my other article Java 8 Functional Programming Tutorial for details about how to use predefined functional interfaces.
4.2 Lambda Expression
Java 8 added a lambda expression (->) to implement a functional interface. In this step, I will create a LambdaDemo
class with three methods:
implementComparator_anonymous
– implements ajava.util.Comparator
functional interface to compare two numbers with an anonymous class.implementComparator_lambda
– implements ajava.util.Comparator
functional interface to compare two integers with a lambda expression.main()
– implements theGreetingFunction
‘s abstract method:speak
with a lambda expression and invokes default methods:hello
() andbye().
LambdaDemo.java
package com.zheng.demo; import java.util.Comparator; public class LambdaDemo { public static void main(String[] args) { GreetingFunction greeting = message -> System.out.println("Hello " + message + "!"); greeting.hello(); greeting.speak("Tom"); greeting.speak("Mary"); greeting.bye(); implementComparator_anonymous(); implementComparator_lambda(); } private static void implementComparator_anonymous() { Comparator<Integer> compareInt = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }; System.out.println(compareInt.compare(3, 4)); } private static void implementComparator_lambda() { Comparator<Integer> compareInt = (o1, o2) -> { return o1.compareTo(o2); }; System.out.println(compareInt.compare(3, 4)); } }
Note:
- line 7 and 30 – implement the only abstract method with a lambda expression.
- line 30 – the implementation with a lambda expression was more compact than the implementation with an anonymous class. This is because there is only one abstract method, so the argument type can be referenced from the only abstract method.
Execute LambdaDemo and capture output as the following:
LambdaDemo Output
Hello! Hello Tom! Hello Mary! Bye! -1 -1
4.3 Stream API
The java.util.stream package supports functional-style operations. In this step, I will create a StreamTest
class to demonstrate how to search, filter, map, and sort elements.
StreamTest.java
package com.zheng.demo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.Before; import org.junit.Test; public class StreamTest { private List<String> userNames; @Test public void test_filter_with_predicate() { Predicate<String> startWithA = name -> name.startsWith("a"); List<String> startWithANames = userNames.stream().filter(startWithA) .collect(Collectors.toList()); assertEquals("aWang", startWithANames.get(0)); } @Test public void test_IntStream_sum() { int sum = IntStream.of(1, 3, 5, 7, 9).sum(); assertEquals(25, sum); } @Test public void test_map_with_methodReference() { List<String> uppercaseNames = userNames.stream().map(String::toUpperCase) .collect(Collectors.toList()); assertTrue(uppercaseNames.contains("MZHENG")); assertTrue(uppercaseNames.contains("AWANG")); assertTrue(uppercaseNames.contains("TCHANG")); } @Test public void test_stream_min_max() { Comparator<String> comparator = Comparator.comparing(String::length); Optional<String> shortestName = userNames.stream().min(comparator); assertTrue(shortestName.isPresent()); assertEquals("aWang", shortestName.get()); Optional<String> longestName = userNames.stream().max(comparator); assertTrue(longestName.isPresent()); assertEquals("mzheng", longestName.get()); } @Test public void test_Stream_foreach() { // Internal iteration userNames.stream().forEach(System.out::println); } @Before public void setup() { userNames = Stream.of("mzheng", "tChang", "aWang").collect(Collectors.toList()); } @Test public void test_stream_sort() { List<String> sortedNames = userNames.stream().sorted().collect(Collectors.toList()); assertEquals("aWang", sortedNames.get(0)); assertEquals("mzheng", sortedNames.get(1)); assertEquals("tChang", sortedNames.get(2)); } }
Notes:
- line 22 – implements a
Predicate
with a lambda expression. - line 23 – uses Stream’s
filter
method. - line 30 – calculates the sum value.
- line 36 – uses Stream’s
map
method. - line 46 – finds the min value.
- line 50 – finds the max value.
- line 59 – uses Stream’s
forEach
method. - line 69 – uses Stream’s
sorted
method
Execute StreamTest as Junit test and capture output here.
mvn test -DTest=StreamTest
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.zheng.demo.DateTest ISO Timestamp: 2021-04-10T07:58:02.297 ISO date: 2021-04-10 Three days date: 2021-04-13T07:58:02.465 Date: 2021-04-05T12:15:00 Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.395 sec Running com.zheng.demo.StreamTest mzheng tChang aWang Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.013 sec Results : Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 15.872 s [INFO] Finished at: 2021-04-10T07:58:02-05:00 [INFO] ------------------------------------------------------------------------
Click my other articles: Stream Map Example and FlatMap Example for details about the map method.
5. Date API
Java 8 provides a new Date-Time API including several new packages which provide a comprehensive date-time model. In this step, I will create a DateTest
class to show several common date operations.
DateTest.java
package com.zheng.demo; import java.time.LocalDateTime; import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import org.junit.Test; public class DateTest { @Test public void test_LocalDateTime_to_formattedString() { LocalDateTime now = LocalDateTime.now(); System.out.println("LocalDateTime now() in ISO_LOCAL_DATE_TIME: " + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(now)); System.out.println("LocalDateTime now() in ISO_LOCAL_DATE: " + DateTimeFormatter.ISO_LOCAL_DATE.format(now)); } @Test public void test_ZonedDateTime() { ZonedDateTime chicagoTime = ZonedDateTime.now(ZoneId.of("America/Chicago")); ZonedDateTime chongqingTime = ZonedDateTime.now(ZoneId.of("Asia/Chongqing")); System.out.println("America/Chicago is at: " + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(chicagoTime)); System.out.println("Asia/Chongqing is at: " + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(chongqingTime)); System.out.println("America/Chicago offset:" + chicagoTime.getOffset()); System.out.println("Asia/Chongqing offset:" + chongqingTime.getOffset()); } @Test public void test_formattedString_to_LocalDateTime() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); LocalDateTime d = LocalDateTime.parse("2021-04-05 12:15", formatter); System.out.println("Date: " + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(d)); } @Test public void test_add_days_to_LocalDateTime() { LocalDateTime newDate = LocalDateTime.now().plus(Period.ofDays(3)); System.out.println( "Three days later: " + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(newDate)); } }
Note:
- line 13 – creates a
now
object fromLocalDatetime.now()
and prints out withDateTimeFormatter
. - line 23 – creates two
ZonedDateTime
variables based on twoZoneId
s and prints out their date and time zone offset. - line 40 – converts a
String
toLocalDateTime
. - line 48 – adds three days to the current date time.
Execute DateTest
as Junit test and capture outputs here.
mvn test -DTest=DateTest
Running com.zheng.demo.DateTest Date: 2021-04-05T12:15:00 America/Chicago is at: 2021-04-11T08:29:03.739-05:00[America/Chicago] Asia/Chongqing is at: 2021-04-11T21:29:03.747+08:00[Asia/Chongqing] LocalDateTime now() in ISO_LOCAL_DATE_TIME: 2021-04-11T08:29:03.778 LocalDateTime now() in ISO_LOCAL_DATE: 2021-04-11 Three days date: 2021-04-14T08:29:03.78 Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.665 sec Running com.zheng.demo.StreamTest mzheng tChang aWang Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 sec Results : Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
Click my other articles: Local date time, Add Days to Date and Compare Date for Date API details.
6. Default Method
Prior to Java 8, an interface can have only abstract methods. Java 8 supports an interface with default methods. The default method has implementation so it won’t break existing implementation classes when adding new default methods. The GreetingFunction
interface at step 4.1 has two default methods.
Click my other article for details about default interface and how to avoid the diamond problem caused by multiple inheritances.
7. Summary
In this example, I demonstrated default methods, functional interfaces, lambda expression, Stream, and Date API. Java 8 maintains its popularity even its latest version is 16 already.
8. Related articles
9. Download the Source Code
You can download the full source code of this example here: Java 8 API Tutorial