Important Features — Java 9, Java 10, Java 11, Java 12, Java13, Java 14, Java 15, Java 16, Java 17

Vijay Aneraye
7 min readFeb 5, 2022

--

New Feature in java9-to Java17

In this article we will go through some of the most interesting features introduced in java in the last few years.

In this article we are not going to cover all features introduced in java in the last few years, instead, We are going to see some of the important feature introduced in the last few years.

Java 9:

- Factory methods for collections(like List, Map, Set and Map.Entry) :

Many a times in your Java program, you have to create a collection (e.g., a List or Set) with some elements. That leads to repetitive coding where you instantiate the collection, followed by several ‘add’ calls. With Java 9, several so-called collection factory methods have been added.

A collection factory method in Java is a static method that provides a simple way of initializing an immutable Collection<E>.

Being immutable, no elements can be added to, removed from, or modified inside the Collection<E> after it is initialized.

List and Set interfaces have “of()” methods to create an empty or no-empty Immutable List or Set objects as shown below:

Example

Empty List example:

List immutableEmptyList = List.of();

Non-Empty List example:

List immutableList = List.of("Abc", "Def", "Ghi");

Empty Map Example:

jshell> Map emptyImmutableMap = Map.of()
emptyImmutableMap ==> {}

Non-Empty Map Example:

Map nonemptyImmutableMap = Map.of(1, "one", 2, "two", 3, "three")
nonemptyImmutableMap ==> {2=two, 3=three, 1=one}

- Private Interface Methods

From Java 9, we can create private methods inside an interface that help to share common code between non-abstract methods.

Example

public interface Person{

private Long createID(){
// Implementation goes here.
}

private static void displayDetails(){
// Implementation goes here.
}

}

Java 10:

- Local variable type inference:

Type inference is a technique used by statically typed languages, where the types of variables may be inferred from context by the compiler.

In JDK 10 and later, you can declare local variables with non-null initializers with the var identifier, which can help you write code that’s easier to read.

Note: that this feature is available only for local variables with the initializer.

Examples:

// Declaration of a local variable in java 10 using LVTIclass A {public static void main(String a[]){
var list = new ArrayList<String>(); // infers ArrayList<String>
var stream = list.stream(); // infers Stream<String>
// Declaration of a local variable in java 10 using LVTI
var x = "Hi there";
// Declaring index variables in for loops using LVTI in Java
for (var x = 0; x < 3; x++) {
System.out.println(x);
}
System.out.println(x)
}
}

This enhancement helps in reducing the boilerplate code; for example:

Map<Integer, String> map = new HashMap<>();

This can now be rewritten as:

var idToNameMap = new HashMap<Integer, String>();

This also helps to focus on the variable name rather than on the variable type.

Illegal Use of var:

  1. “var” won’t work without the initializer and not with Null :
var name; // error: cannot use 'var' on variable without initialize
var emptyList = null; /error

2. “var” cannot use with lambda express and array list because lambda expression and array list needs an explicit target type

var expression = (String s) -> s.length() > 10; // error: lambda expression needs an explicit target-typevar arrayList = { 1, 2, 3 }; // error: array initializer needs an explicit target-type

3. Other Error cases

class A {//Not permitted in class fields(non-local variables)
public var = "hello"; // error: 'var' is not allowed here
//Not allowed as parameter for any methods
void show(var a) /*Error:
{
}
//Not permitted in method return type
public var show() /* Error: {
return 1;
}
}

Java 11:

- New utility methods in String class

Java 11 adds a few new methods to the String class: isBlank, lines, strip, stripLeading, stripTrailing, and repeat.

  • isBlank(): This is a boolean method. It just returns true when a string is empty and vice-versa.
Example
class Example {
String str1 = "";
System.out.println(str1.isBlank());
}
output: True
  • lines(): This method returns a stream of strings, which is a collection of all substrings split by lines.
Example
class Example {
String str = "Hi\nHello\nNamaste";
System.out.println(str.lines().collect(Collectors.toList()));
}
Output:
Hi
Hello
Namaste
  • repeat(n): Result is the concatenated string of original string repeated the number of times in the argument.
Example
class Example {
String str = "1".repeat(5);
System.out.println(str);
}
Output:
11111
  • strip(): It is used to remove the white-spaces which are in-front and back of the string.strip() is “Unicode-aware” evolution of trim(). When trim() was introduced, Unicode wasn’t evolved. Now, the new strip() removes all kinds of whitespaces leading and trailing(check the method Character.isWhitespace(c) to know if a unicode is whitespace or not)
  • stripTrailing(): It is used to remove the white-space which is in back of the string
  • stripLeading(): It is used to remove the white-space which is in-front of the string
Examples:public class HelloWorld {
public static void main(String[] args)
{
System.out.println(" hi ".strip());
System.out.println(" hi ".stripLeading());
System.out.println(" hi ".stripTrailing());
}
}
Output:
"hi"
"hi "
" hi"

- New File Methods

Using these readString and writeString static methods from the Files class, Java 11 aims to reduce a lot of boilerplate code which makes much easier to read and write files.

public class FileReadWriteTest{@Test                                 
void readFileAndWriteFileTest() throws IOException {

Path filePath = Files.writeString(Files.createTempFile(tempDir, "demo", ".txt"), "Sample text");

String fileContent = Files.readString(filePath); assertThat(fileContent).isEqualTo("Sample text"); }
}

- The Not Predicate Method

A static not method has been added to the Predicate interface. We can use it to negate an existing predicate, much like the negate method:

List<String> sampleList = Arrays.asList("Java", "\n \n", "Kotlin", " ");List withoutBlanks = sampleList.stream()
.filter(Predicate.not(String::isBlank))
.collect(Collectors.toList());
assertThat(withoutBlanks).containsExactly("Java", "Kotlin");

Java 12:

- Changes in Switch expressions:

Before Java 12, switch was a statement only. Now, though, you can also have switch expressions. For example, take a look at this code that processes various user events:

Switch expressions will now be used as a statement as well as expressions. This makes code simplification and pattern matching possible for the switch.

Now, the new Arrow syntax for switch introduced as

case X -> {}

Firstly, let’s look and at the old syntax:

DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();String typeOfDay = "";
switch (dayOfWeek) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
typeOfDay = "Working Day";
break;
case SATURDAY:
case SUNDAY:
typeOfDay = "Day Off";
}

And now, let’s see the same logic witch switch expressions:

typeOfDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
case SATURDAY, SUNDAY -> "Day Off";
};

Note: how the switch now returns a value

Java 14:

- Record type

A record is a data class that stores pure data. The idea behind introducing records is to quickly create simple and concise classes devoid of boilerplate code in the model POJOs.

public record PersonRecord(String name, int age){}

PersonRecord personRecord = new Personrecord("Abc",1)
System.out.println(personRecord.age())

The important difference between class and record is that a record aims to eliminate all the boilerplate code needed to set and get the data from instance. Records transfer this responsibility to java compiler which generates the constructor, field getters, hashCode() and equals() as well toString() methods.

- Helpful NullPointerExceptions

Previously, the stack trace for a NullPointerException didn’t have much of a story to tell except that some value was null at a given line in a given file.

Though useful, this information only suggested a line to debug instead of painting the whole picture for a developer to understand, just by looking at the log.

For example, consider this simple snippet:

int[] arr = null;
arr[0] = 1;

Earlier, on running this code, the log would say:

Exception in thread "main" java.lang.NullPointerException
at com.baeldung.MyClass.main(MyClass.java:27)

But now, given the same scenario, the log might say:

java.lang.NullPointerException: Cannot store to int array because "a" is null

As we can see, now we know precisely which variable caused the exception.

- Pattern Matching for instanceof :

Java developer makes good use of instanceof conditions throughout the code. Specifically, an instanceof conditional check is generally followed by a typecasting.

Java 14, gets rid of this verbosity by making conditional extraction a lot more concise.

Before Java 14:

if (obj instanceof Journaldev) {
Journaldev jd = (Journaldev) obj;
System.out.println(jd.getAuthor());
}

Java 14 Onwards:

if (obj instanceof Journaldev jd) {     
System.out.println(jd.getAuthor());
}

Java 15:

- Text Blocks

Text blocks are finally a standard feature in Java 15. A text block is a multi-line string literal.

While using text blocks, we do not need to provide explicit line terminators, string concatenations, and delimiters otherwise used for writing the normal string literals.

Text blocks start with a “”” (three double-quote marks) followed by optional whitespaces and a newline. The most simple example looks like this:

String example = """
Example text
""";
String json = """
{
"name": "abc",
"age":2
},
{
"name": "xyz",
"age":23
}
""";
String dbSchema = """
CREATE TABLE 'TEST'.'EMPLOYEE'(
'ID' INT NOT NULL DEFAULT 0 ,
'FIRST_NAME' VARCHAR(100) NOT NULL ,
'LAST_NAME' VARCHAR(100) NULL ,
'STAT_CD' TINYINT NOT NULL DEFAULT 0);
""";

Java 16:

- Add Stream.toList Method

The aim is to reduce the boilerplate with some commonly used Stream collectors, such as Collectors.toList and Collectors.toSet:

List<String> integersAsString = Arrays.asList("1", "2", "3");//Old Way
List<Integer> ints = integersAsString.stream().map(Integer::parseInt).collect(Collectors.
toList());
//From java16 onwords
List<Integer> intsEquivalent = integersAsString.stream().map(Integer::parseInt).toList();

Java 17:

- Sealed Classes and Sealed Interfaces:

Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them.

Syntax:

public abstract sealed class ClassName
permits Class1, Class2, Class3 {...}

Note: Only final,sealed or non-sealed class can extend sealclass

Example:

class sealed abstract Write permits FileWrite {//declaration}class final FileWrite extends Write {} //compileclass sealed abstract IOWrite extends Write permits DBWrite{}//compileclass CDWrite extends Write {} //*error, only  final,sealed or non-sealed class can extend sealclassinterface sealed Drive permits HDDrive{}

class non-sealed HDDrive implements Drive {}

These are some of the important java features which were introduced in different java releases.

--

--