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.Comparatorfunctional interface to compare two numbers with an anonymous class.implementComparator_lambda– implements ajava.util.Comparatorfunctional interface to compare two integers with a lambda expression.main()– implements theGreetingFunction‘s abstract method:speakwith 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
Predicatewith a lambda expression. - line 23 – uses Stream’s
filtermethod. - line 30 – calculates the sum value.
- line 36 – uses Stream’s
mapmethod. - line 46 – finds the min value.
- line 50 – finds the max value.
- line 59 – uses Stream’s
forEachmethod. - line 69 – uses Stream’s
sortedmethod
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
nowobject fromLocalDatetime.now()and prints out withDateTimeFormatter. - line 23 – creates two
ZonedDateTimevariables based on twoZoneIds and prints out their date and time zone offset. - line 40 – converts a
StringtoLocalDateTime. - 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


