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 theinstanceof
the method supports pattern matching. - Java API –
Stream
API 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
s
in theelse
block. - line 36, 39 – can access the variable
s
in theelse
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()
.
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
instanceof
method. - 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