Home » Core Java » Java 16 New Features Tutorial

About 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.

Java 16 New Features Tutorial

1. Introduction

Java 16 has a list of new features. In this tutorial, I will demonstrate the following new features:

  • Java language – add a new java.lang.record type and the instanceof the method supports pattern matching.
  • Java API – Stream API adds toList() and mapMulti() methods.

2. Technologies Used

The example code in this article was built and run using:

  • Java 16
  • Eclipse 4.19

3. Record

Java 16 language add a new record type that deals with immutable data and provides constructor, getters, toString, equals, and hashCode methods automatically. Here is the syntax:

$accessModifier record $recordName ($parameters){ $body }

  • $accessModifier – can be public, private, or protected.
  • $recordName – follows Java class naming rules.
  • $parameters – follows Java variable naming rules.

3.1 Simple Record

In this step, I will create a SimpleRecord type which has two fields: name and isDone.

SimpleRecord.java

package org.jcg.zheng.lang.demo;

/**
 * record is new language type to deal with immutable data without any
 * boiler-plate code: constructor, access, toString, equals, and hashCode
 * 
 *
 */
public record SimpleRecord(String name, boolean isDone) {

}

As you seen in Figure 1, compiler automatically provides name(), isDone(), toString(), equals(), hashCode(). It eliminates the boiler-plate code.

Java 16 - generated methods
Figure 1 SimpleRecord’s Generated Methods

3.2 Complex Record

In this step, I will create a ComplexRecord which implements from two interfaces: AInterface and Serializable. It also contains a SimpleRecord and validates the data.

ComplexRecord.java

package org.jcg.zheng.lang.demo;

import java.io.Serializable;
import java.util.Objects;

public record ComplexRecord(String title, String alias, int age, SimpleRecord simpleRecord)
		implements AInterface, Serializable {

	//can have static fields
	static String department = "Math";

	@Override
	public String whoAreyou() {
		return "I am ComplexRecord. " + toString();
	}

	//create a new access
	public String completedName() {
		return title + " " + alias;
	}

	//override the generated getter
	public String title() {
		return title.toUpperCase();
	}

	//overwrite constructor with validation
	public ComplexRecord {
		if (age < 0) {
			throw new IllegalArgumentException("age must be a positive number.");
		}

		Objects.requireNonNull(title);
	}

}

  • line 10 – adds a static field.
  • line 18 – creates a new method transforming the data.
  • line 23 – overwrites the generated method.
  • line 28 – creates a constructor with validation logic.

Here is the interface which ComplexRecord implements.

AInterface.java

package org.jcg.zheng.lang.demo;

public interface AInterface {
	
	String whoAreyou();

}

3.3 Demo Record

In this step, I will create a RecordDemoApp class which shows the usage of the SimpleRecord and ComplexRecord.

RecordDemoApp.java

package org.jcg.zheng.lang.demo;

import java.util.HashMap;
import java.util.Map;

public class DemoRecordApp {

	public static void main(String[] args) {
		SimpleRecord simpleRed = new SimpleRecord("English", false);
		System.out.println(simpleRed.name());

		ComplexRecord redComplex = new ComplexRecord("Manager", "bob", 12, simpleRed);
		System.out.println(redComplex.toString());
		System.out.println(redComplex.completedName());
		System.out.println(redComplex.whoAreyou());
		System.out.println(redComplex.title());
		System.out.println(redComplex.age());

		Map<SimpleRecord, ComplexRecord> test = new HashMap<>();
		test.put(simpleRed, redComplex);
		
		System.out.println("test map value=" + test.get(simpleRed));

		try {
			ComplexRecord bad = new ComplexRecord("Dead", "People", -5, simpleRed);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

}

Execute it as a Java application and capture the output here.

RecordDemoApp output

English
ComplexRecord[title=Manager, alias=bob, age=12, simpleRecord=SimpleRecord[name=English, isDone=false]]
Manager bob
I am ComplexRecord. ComplexRecord[title=Manager, alias=bob, age=12, simpleRecord=SimpleRecord[name=English, isDone=false]]
MANAGER
12
test map value=ComplexRecord[title=Manager, alias=bob, age=12, simpleRecord=SimpleRecord[name=English, isDone=false]]
age must be a positive number.

4. Pattern Matching for instanceof

Pattern matching eliminates object cast when using the instanceof method. In this step, I will create an InstanceofDemo class to show the Java 16 and older way of the instanceof method.

InstanceofDemo.java

package org.jcg.zheng.lang.demo;

public class InstanceofDemo {

	public static void main(String[] args) {
		instanceOfMethod("Zheng");
		instanceOfMethod(100);

		instanceOfMethod16("Mary");
		instanceOfMethod16(Integer.valueOf(50));

		instanceOfMethod16Else("JCG");
		instanceOfMethod16Else(15);
	}

	private static void instanceOfMethod16(Object obj) {
		if (obj instanceof String s) {
			System.out.println("It's a string " + s);
		} else if (obj instanceof Integer i) {
			System.out.println("It's a number " + i);
			// System.out.println("can not see variable s here " + s);
		}
	}

	private static void instanceOfMethod(Object obj) {
		if (obj instanceof String) {
			String s = (String) obj;
			System.out.println("It's a string " + s);
		} else {
			System.out.println("It's not a string ");

		}
	}

	private static void instanceOfMethod16Else(Object obj) {
		if (!(obj instanceof String s)) {
			System.out.println("It's not a string ");
		} else {
			System.out.println("It's a string " + s);

		}
	}

}
  • line 17, 21 – can not access the variable s in the else block.
  • line 36, 39 – can access the variable s in the else block

Execute it as a Java application and capture output here.

InstanceofDemo output

It's a string Zheng
It's not a string 
It's a string Mary
It's a number 50
It's a string JCG
It's not a string 

5. Stream API

Java 16 enhances Stream API with toList() and mapMulti() methods. Developers can write less code when converting the stream to a list.

See Figure 2 for the detail of Stream.toList().

Java 16 - stream to list
Figure 2 Stream toList Method

See Figure 3 for the detail of Stream.mapMulti().

Java 16 - stream.mapmulti
Figure 3 Stream.mapMulti Method

In this step, I will create a StreamDemo class which uses both toList and mapMulti methods.

StreamDemo.java

package org.jcg.zheng.api.demo;

import java.util.List;
import java.util.stream.Collectors;

public class StreamDemo {

	public static void main(String[] args) {

		List<String> testList = List.of("mary", "zheng", "Test");

		CollectorToList(testList);

		StreamToList(testList);

		testList.stream().mapMulti((name, downstream) -> downstream.accept(name.replace("a", "-Number1-")))
				.forEach(System.out::println);
	}

	private static void StreamToList(List<String> testList) {
		List<String> immutableList = testList.stream().filter(name -> name.contains("e")).toList();
		System.out.println(immutableList);
		try {
			immutableList.add("t");
		} catch (Exception e) {
			// java.lang.UnsupportedOperationException as this containsAList is immutable
			e.printStackTrace();
		}
	}

	private static void CollectorToList(List<String> testList) {
		List<String> mutableList = testList.stream().filter(name -> name.contains("e")).collect(Collectors.toList());

		mutableList.add("JCG");

		System.out.println(mutableList);
	}

}
  • line 24 – Stream.toList() returns a unmodified list, so can not add a new element.

Run it as a Java application and capture the output here.

StreamDemo output

[zheng, Test, JCG]
[zheng, Test]
java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at org.jcg.zheng.api.demo.StreamDemo.StreamToList(StreamDemo.java:24)
	at org.jcg.zheng.api.demo.StreamDemo.main(StreamDemo.java:14)
m-Number1-ry
zheng
Test

6. Summary

In this example, I demonstrated the following new features in Java 16 language and API:

  • Java language introduces a new record type.
  • Pattern matching with the instanceof method.
  • Stream has several new methods: toList() and mapMulti().

8. Download the Source Code

Download
You can download the full source code of this example here: Java 16 New Features Tutorial

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area

 

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