Core Java

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.

java 8 api - java 8 stream
Figure 1 Java 8 Stream API and Functional Interfaces

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 a java.util.Comparator functional interface to compare two numbers with an anonymous class.
  • implementComparator_lambda – implements a java.util.Comparator functional interface to compare two integers with a lambda expression.
  • main() – implements the GreetingFunction‘s abstract method: speak with a lambda expression and invokes default methods: hello() and bye().

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 from LocalDatetime.now() and prints out with DateTimeFormatter.
  • line 23 – creates two ZonedDateTime variables based on two ZoneIds and prints out their date and time zone offset.
  • line 40 – converts a String to LocalDateTime.
  • 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.

9. Download the Source Code

Download
You can download the full source code of this example here: Java 8 API Tutorial

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button