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.recordtype and theinstanceofthe method supports pattern matching. - Java API –
StreamAPI addstoList()andmapMulti()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, orprotected. - $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.
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
sin theelseblock. - line 36, 39 – can access the variable
sin theelseblock
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().
See Figure 3 for the detail of Stream.mapMulti().
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
instanceofmethod. - Stream has several new methods:
toList()andmapMulti().
7. More articles
- How to update Java for Windows 10, macOS, and Android
- Java 8 Features Tutorial
- Download and Install Java Development Kit (JDK) 11
- Download and Install Java Development Kit (JDK) 13
8. Download the Source Code
You can download the full source code of this example here: Java 16 New Features Tutorial



