Java 17 Features

Java 17 Features

Sealed Classes & Interfaces

  • Limiting the extensibility of classes and interfaces, which means that can specify which other classes or interfaces are permitted to extend or implement them.
Sealed Classes
public sealed class Shape permits Circle, Square {}
public non-sealed class Square extends Shape{}
public non-sealed class Circle extends Shape {}
  • Sealed keywords to the class and specify which classes are permitted to inherit it by using the “permits keyword to the class

  • Shape is the Parent class which we specify to permit which classes can inherit,here the child classes are Square and Circle

-Child classes of sealed class should be either sealed, non-sealed or final, here

public sealed class Shape permits Circle,Square{
	public void printName() {
		System.out.println("Default");
	}}

public non-sealed class Circle extends Shape {
	public void printName() {
		System.out.println("Circle");
	}}


public non-sealed class Square extends Shape{
	public void printName() {
		System.out.println("Square");
	}}

Switch Statements

  • Java.lang.Object is now supported in switch case with simplified return statement without having to use the return statement again.Below is the demo code for the demonstration which shows the traditional way of switch case which only supports int,byte,short. We can also make use of the switch case functionality over objects
package java17features;
public class SwitchExamples {
	// traditional Switch statement
	public String traditionalSwitch(int dayNum) {
		String day;
		switch (dayNum) {
		case 1:
			day = "Monday";
			break;
		case 2:
			day = "Tuesday";
			break;
		default:
			throw new IllegalArgumentException("Unexpected value: " + dayNum);
		}
		return day;
	}

	public String simpleSwitch(String dayNum) {
		return switch (dayNum) {
		case "Monday" -> "Week day";
		case "Tuesday" -> "Week day";
		case "Wednesday" -> "Week day";
		case "Thursday" -> "Week day";
		case "Friday" -> "Week day";
		// multiple case values in single case label statements.
		case "Saturday", "Sunday" -> "Weekend";
		default -> "Unknown";
		};
	}
	public Object getLength(Object obj) {
		return switch (obj) {
		case Integer i -> "It is an integer";
		case String s -> "It is a string";
		case Shape s -> "It is a Shape";
		default -> "It is none of the known data types";
		};
		
	}}

InstanceOf Pattern Matching

  • Reducing redundant casting during the creation of objects.
  • From java17 onwards we can do pattern matching without the need to need to do redundant casting for objects which can be done within the if condition Here, String s is tested and casted within the if statement avoiding the redundant casting in traditional way.

//tradition way,redundant casting in line 8
	void testInstanceOf(Object obj) {
		if(obj instanceof String) {
			String s = (String) obj;
			System.out.println(s.length());
		}
	}
	
	// testing the instanceOf object in jdk17
	void testInstanceOf2(Object obj) {
		if(obj instanceof String s) {
			System.out.println(s.length());
		}

Switch Expressions

  • Pattern Matching using switch Expressions.Below is the example for switch expression feature in switch statement.
    public static double getPerimeter(Shape shape) throws IllegalArgumentException {
        switch (shape) {
            case Rectangle r: return 2 * r.length() + 2 * r.width();
            case Circle c:    return 2 * c.radius() * Math.PI;
            default:          throw new IllegalArgumentException("Unrecognized shape");
        }
    }

Record Classes

Passing immutable data between objects is the most common operation that is done, there’s a lot of boilerplate code involved like getters, setters. We write classes to simply hold data such as database results, query results. Immutability ensures validity of objects. To accomplish immutability between objects we use –

  • private, final field for each piece of data.
  • Getter for each field
  • Public constructor for each field.
  • Equals,hashCode and toString methods. We can reduce the number of lines using Lombok,since Lombok itself being a community project, relying on it as a dependency and backward compatibility issues within the JDK version can lead to complex problems. Lombok is a third party dependency which relies on community support for feature and development. To overcome this issue we use Records which can hold immutable data classes that require only type and name of fields.

We create a record using its fields, the JDK compiler creates getters and setters and methods for Person avoiding the boilerplate code here. The Attributes of Person are accessed by ObjectName.attributename(), unlike get and set methods. Since Records are immutable objects we cannot change or set values to attributes once initialized.

 public record Person(int id,String firstName,String lastName) {}
System.out.println("Java 17 features: Records for immutability in Objects");
Person person= new Person(1, "Vinay", "Keshava");
Person person1 = new Person(2,"Mahesh","Kumar");
System.out.println(person.firstName());
System.out.println(person.lastName());
System.out.println(person.id());
System.out.println(person.equals(person1)); 
//false : since both are different objects

Static variables and methods can be declared within the records. JDK compiler also create a default constructor for the record created.

TextBlocks

  • Textblock is to provide clarity by way of minimizing the java syntax required to render a string that spans multiple lines. In earlier releases of JDK explicit line terminators, concatenation and delimiters were used to implement multi line strings.
// text blocks
		System.out.println("""
				multi Line string
				using text blocks
				textblocks can also be used as method as in println
				""");

Strong Encapsulation of JDK Internal API’s

  • From JDK 9 to 15, code from classpath could still access internal JDK API’s, applications with this issue were managed with the command line options, –illegal-access. From JDK 17 onwards the access to JDK was deactivated, and “—add-exports” , “—add-open” gives access to internal API’s.

Foreign Function and Memory API in java

  • Foreign Function Interface and Memory API in java provide mechanisms to interact with libraries of other languages such as C or C++. This allows developers to leverage existing native libraries or access low level system libraries. For example a native library written in C with .so or .a files and accessing these over java is possible over foreign function interface.

Local Variable Type Inference

  • Type inference refers to the automatic detection of the datatype of a variable, done generally at the compile time.

The traditional way of creating an object was:

Class_name object_name = new Class_name(arguments);

This is how things have been since the inception of java. If the type of object is clearly mentioned on the right side of the expression, mentioning the same thing before the name of the variable makes it redundant. Therefore the need to eliminate the redundancy and make variable declaration shorter.

Initialization is mandatory on the right hand side of var. var order = “first”;

here the right hand side of the assignment operator is String,so there’s need to declare the variable order as String on the left hand side, type inference comes into picture here. To make code readability easier, can declare local variables as non-null initializers with the “var” identifier.

Local variable type inference can be used in the traditional for loop can be for initialization.