Java — Recent Developments

Features to Enhance Developer Friendliness

Satheesh
Insights from ThoughtClan
4 min readSep 2, 2021

--

In the last article on “Java — Incorporation of Functional Programming Concepts”, I had explored the functional programming features introduced in Java 8 and above.

In this article, let me explore some other features introduced in recent versions of Java aimed at making Java more developer friendly. Many improvements have been made including fixing some of the standout issues.

Text Blocks

Java 14 introduced text blocks which eases initialisation with a multi-line string. This is done by enclosing the multi-line string within “”” and “””.

var html  = """
<html>
<head>
<title>This is a HTML snippet</title>
</head>
<body>
</body>
</html>
""";

Switch Expressions

Switch Expressions were introduced with Java 14; older switch statements were always a pain to write and maintain and the newer version aims to fix those issues, mainly:

  • We do not have to write break statements in each case, as the new switch expression does not fall through like the earlier version.
  • A new keyword yield has been introduced which can be used to return a value at the end of multi-line case blocks.
  • Single line case blocks can use the lambda notation ‘->’ to return value immediately.
  • Switch expressions are treated as “expressions” and can be used in all places where an expression can be used e.g., can be passed as input to a function call and can be returned as output.

Below is an example switch statement depicting all the above features.

var s = "foo";
System.out.println( switch (s) {
case "foo", "bar" -> {
System.out.println( "Can execute some logic here." );
yield 2;
}
default -> 3;
} );

Records

Java 16 has introduced the ability to define “record” classes which can be used to define POJOs. Following is an example:

record Mark(Integer studentId, Long mark) {};

Following rules are applied automatically:

  • All fields declared above are private and final.
  • A canonical constructor is automatically defined considering the fields defined above.
  • Similarly, toString, equals and hashCode methods too are defined considering all the fields defined above. They can be overridden to provide custom logic.
  • Records themselves cannot extend from other classes, are final and can’t be abstract.
  • Getters generated for all fields.

This may not be all that interesting for developers who are used to tools like Project Lombok which do this and more. What makes it interesting is that records can be declared directly as local variables. This will come in handy when we need a data carrier but in only one method; avoids having to create a java file just for this.

public void myComplexMethod() {
// do something
// declare record as local variable
record Mark(Integer studentId, Long mark) {};
// use it
final Mark mark = new Mark(student.id(), 50L);
// do something
}

Pattern Matching for ‘instanceof’

One of the other annoying things was that we had to do class cast for an object even when we have used instanceof to check if the object is of a certain class. This is not required anymore since Java 16. One can do this like below:

if (obj instanceof String s && s.length() > 5) {
// use s directly as string
}

Notice that along with instanceof, we are able to assign it to a variable and use it thereafter in the rest of the if condition and within the if block.

When we have the below statement, the variable will be available after the if block and not within the if block. This is because the if condition is negative; note that the if block should have returned from the method or thrown an exception.

if (!(obj instanceof String s)) { 
throw new IllegalArgumentException(); // or return something;
}
// s is in scope - because instanceof was able to cast the object to string
// in case if it was not able to and condition fails above, the execution will not reach here as the if block throws an exception

‘var’ Keyword

‘var’ keyword is used in JavaScript and Kotlin to declare a variable. Java introduced this keyword for usage with method local variables, as part of Java 10. This helps in avoiding having to write longer lines / duplicating classnames when we declare and initialize a new object. E.g.,

List<String> values = new ArrayList<>(); can be now written as var values = new ArrayList<String>();

But unlike JavaScript, the type of a variable cannot change after the initial declaration and initialization. Java is still a statically typed language.

Private Methods in Interfaces

While Java 8 introduced default methods in interfaces, Java 9 introduced private methods. This is very useful if we want to reuse logic across different default methods in an interface.

Factory Methods for Initialising Collections

In Java 9, Factory methods were introduced in the Collections framework to ease initialisation with constant values. E.g.,

List.of(“1”, “2”, “3”)

Earlier we had to initialise an empty list and add values one by one or initialise an array and convert to a list.

Above are some enhancements done to the language syntax to improve ease of use for developers. In the next article in this series “Modules & Leaner Runtime”, I have explored Java Platform Modules System (JPMS).

--

--