Showing posts with label java15. Show all posts
Showing posts with label java15. Show all posts

Thursday, 28 July 2022

Java15: text blocks

Text blocks is a preview feature in Java13 and 14, it is now supported as product feature in Java15. You can learn more about this here.


Previous                                                 Next                                                 Home

Java15: Pattern matching type checks

The pattern matching feature was added as a preview feature in Java14, there are no further enhancements in Java 15. You can learn more about this here.

 

 

References

https://openjdk.org/jeps/375

Previous                                                 Next                                                 Home

How to get the record components in Java?

Records are classes to define immutable data. This is added as a preview feature in Jdk15. With records, we can create  immutable classes in a compact way.

public record Employee(int id, String name) {
    
}

 

The declaration of record specifies

a.  Name -> Employee

b.  Header -> (int id, String name)

c.  Body -> {}

 

Name specifies the record name, and header lists the components of the record, which are the variables that make up its state. We can access the record components using getRecordComponents() method available in java.lang.Class.

 


Point.java

package com.sample.app.reflections;

public record Point(int x, int y) {

}

 

RecordComponentsDemo.java

package com.sample.app.reflections;

import java.lang.reflect.RecordComponent;

public class RecordComponentsDemo {
    
    public static void main(String[] args) {
        RecordComponent[] recordComponents = Point.class.getRecordComponents();
        
        for(RecordComponent recordComponent: recordComponents) {
            System.out.println(recordComponent);
        }
    }

}

 

Output

int x
int y

 

References

https://openjdk.org/jeps/384

 


Previous                                                 Next                                                 Home

How to check whether a type is record or not?

'java.lang.Class' provides 'isRecord' method which returns true if and only if this class is a record class, otherwise false.

 

Signature

public boolean isRecord()

 


 

 

Point.java

package com.sample.app.reflections;

public record Point(int x, int y) {

}

 

RecordTypeCheck.java

package com.sample.app.reflections;

public class RecordTypeCheck {

    public static void main(String[] args) {
        boolean isRecord = Point.class.isRecord();

        System.out.println("Is Point record type? " + isRecord);
    }

}

 

Output

Is Point record type? true

 

References

https://openjdk.org/jeps/384

 

 

Previous                                                 Next                                                 Home

Sealed classes, interfaces and records

Sealed classes, interfaces work well with records. To demonstrate the example, I am defining a sealed interface Operation, and two record types Add, Mul will implement this sealed interface.

 


Operation.java

package com.sample.app.recrods;

public sealed interface Operation permits Add, Mul{

    public Long operation();
}

Add.java

package com.sample.app.recrods;

public record Add(int... a) implements Operation {

    @Override
    public Long operation() {
        if(a == null || a.length == 0) {
            return null;
        }
        
        long sum = 0;
        
        for(int ele : a) {
            sum += ele;
        }
        
        return sum;
    }

}

Mul.java

package com.sample.app.recrods;

public record Mul(int... a) implements Operation {

    @Override
    public Long operation() {
        if (a == null || a.length == 0) {
            return null;
        }

        long result = 1;

        for (int ele : a) {
            result *= ele;
        }

        return result;
    }

}

SealedAndRecordDemo.java

package com.sample.app;

import com.sample.app.recrods.Add;
import com.sample.app.recrods.Mul;

public class SealedAndRecordDemo {

    public static void main(String[] args) {
        Add add = new Add(new int[] { 2, 3, 5, 7 });
        Mul mul = new Mul(new int[] { 2, 3, 5, 7 });

        System.out.println("Sum of first four primes : %d".formatted(add.operation()));
        System.out.println("Multiplication of first four primes : %d".formatted(mul.operation()));
    }

}

Output

Sum of first four primes : 17
Multiplication of first four primes : 210

 

References

https://openjdk.org/jeps/360

https://openjdk.org/jeps/384

 



 

Previous                                                 Next                                                 Home

Java15: Records

Records are classes to define immutable data. This is added as a preview feature in Jdk15.

 

Let’s learn more about records using an example.

 

Without records

Prior to records, we define an immutable class like below.

 

Employee.java

package com.sample.app.recrods;

import java.util.Objects;

public final class Employee {

	private final int id;
	private final String name;

	public Employee(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	@Override
	public int hashCode() {
		return Objects.hash(id, name);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		return id == other.id && Objects.equals(name, other.name);
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + "]";
	}

}

 

With records, we can create same Employee immutable class in a compact way.

public record Employee(int id, String name) {
	
}

 

The declaration of record specifies

a.   Name -> Employee

b.   Header -> (int id, String name)

c.    Body -> {}

 

Name specifies the record name, Header specifies the properties of the record.

 

Let’s decompile the Employee class file and explore further

Compile Employee.java class by enabling preview features.

$javac  --release 15 --enable-preview Employee.java
Note: Employee.java uses preview language features.
Note: Recompile with -Xlint:preview for details.

 

Let’s decompile Employee.cass file.

$javap Employee.class                              
Compiled from "Employee.java"
public final class com.sample.app.recrods.Employee extends java.lang.Record {
  public com.sample.app.recrods.Employee(int, java.lang.String);
  public final java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int id();
  public java.lang.String name();
}

From the above snippet, you can deduce.

a.   Records implicitly extends java.lang.Records class.

b.   For each component or variable in the record header (id, name),

1.   A private final field is defined.

2.   A public access method with the same name as variable name is defined.

c.    Override the implementation of hashCode, equals and toString methods.

 

How to get an instance of Employee record?

Similar to a class, a record is instantiated with the new keyword.

 

Employee emp1 = new Employee(1, "Krishna");

 

RecordDemo.java

package com.sample.app;

import com.sample.app.recrods.Employee;

public class RecordDemo {

	public static void main(String[] args) {
		Employee emp1 = new Employee(1, "Krishna");

		System.out.println("id = %d".formatted(emp1.id()));
		System.out.println("name = %s".formatted(emp1.name()));
	}

}

Output

id = 1
name = Krishna

When you define a record, compiler provide some default implementation to hashCode, equals and toString methods.

You can confirm the same from below application.

 

RecordDemo1.java

package com.sample.app;

import com.sample.app.recrods.Employee;

public class RecordDemo1 {
	
	public static void main(String[] args) {
		Employee emp1 = new Employee(1, "Krishna");
		Employee emp2 = new Employee(2, "Krishna");
		Employee emp3 = new Employee(1, "Krishna");

		System.out.println("id = %d".formatted(emp1.id()));
		System.out.println("name = %s".formatted(emp1.name()));

		System.out.println("\nemp1.hashCode() = %d".formatted(emp1.hashCode()));
		System.out.println("emp1.toString() =  %s".formatted(emp1.toString()));

		System.out.println("\nemp2.hashCode() = %d".formatted(emp2.hashCode()));
		System.out.println("emp2.toString() =  %s".formatted(emp2.toString()));

		System.out.println("\nemp3.hashCode() = %d".formatted(emp3.hashCode()));
		System.out.println("emp3.toString() =  %s".formatted(emp3.toString()));

		System.out.println("\nemp1.equals(emp2) = %s".formatted(emp1.equals(emp2)));
		System.out.println("emp1.equals(emp3) = %s".formatted(emp1.equals(emp3)));
	}
	
}

Output

id = 1
name = Krishna

emp1.hashCode() = 1207521705
emp1.toString() =  Employee[id=1, name=Krishna]

emp2.hashCode() = 1207521736
emp2.toString() =  Employee[id=2, name=Krishna]

emp3.hashCode() = 1207521705
emp3.toString() =  Employee[id=1, name=Krishna]

emp1.equals(emp2) = false
emp1.equals(emp3) = true

How to add validations to the record properties?

we can define a canonical constructor that perform some validations.

 

A canonical constructor  signature is the same as the header, and which assigns each private field to the corresponding argument from the new expression which instantiates the record;

 

Employee.java

package com.sample.app.recrods;

public record Employee(int id, String name) {

	public Employee(int id, String name) {
		if (id <= 0) {
			throw new IllegalArgumentException("id must be > 0");
		}

		if (name == null || name.isBlank()) {
			throw new IllegalArgumentException("name should not be null or blank");
		}

		this.id = id;
		this.name = name;
	}

}

 

Compact canonical constructor

A compact canonical constructor omit the list of formal parameters as they are declared implicitly, and the private fields corresponding to record components cannot be assigned in the body but are automatically assigned to the corresponding formal parameter (this.id = id;) at the end of the constructor.

 

For example, following snippet use a compact canonical constructor that validates its (implicit) formal parameters.

 

Employee.java 

package com.sample.app.recrods;

public record Employee(int id, String name) {

	public Employee {
		if (id <= 0) {
			throw new IllegalArgumentException("id must be > 0");
		}

		if (name == null || name.isBlank()) {
			throw new IllegalArgumentException("name should not be null or blank");
		}
	}

}

 

Constructor rules are different for a class and a record

The rules for constructors are different in a record than in a normal class. A normal class without any constructor declarations is automatically given a default constructor. In contrast, a record without any constructor declarations is automatically given a canonical constructor that assigns all the private fields to the corresponding arguments of the new expression which instantiated the record.

 

Can I overload the constructors in a record?

Yes, you can overload the constructors. But keep in mind that a non-canonical constructor must start with an explicit invocation to a constructor.

package com.sample.app.recrods;

public record Employee(int id, String name) {

	public Employee() {
		this(Integer.MAX_VALUE, "");
	}

	public Employee(int id) {
		this(id, "");
	}

	public Employee(String name) {
		this(Integer.MAX_VALUE, name);
	}

}

 

Can a record is nested in another record?

Yes, a record can be declared top level or nested, and can be generic. If a record is itself nested, then it is implicitly static

package com.sample.app.recrods;

public record Employee(int id, String name) {

	public record Person(){
		
	}

}

 

Can a record define static methods, static blocks and static fields?

Yes, it can.

 

Employee.java

package com.sample.app.recrods;

public record Employee(int id, String name) {
	
	static {
		System.out.println("Employee record loaded");
	}

	private static int count = 0;

	public Employee {
		count++;
	}

	public static int empCount() {
		return count;
	}

}

 

RecordDemo3.java

package com.sample.app;

import com.sample.app.recrods.Employee;

public class RecordDemo3 {
	
	public static void main(String[] args) {
		Employee emp1 = new Employee(1, "Krishna");
		Employee emp2 = new Employee(2, "Ram");
		
		System.out.println("Total employees : "+ Employee.empCount());
	}

}

 

Output

Employee record loaded
Total employees : 2

 

Can a record has instance methods?

Yes, you can define instance methods in a record. A record can explicitly declare public accessor methods which correspond to components, and can also declare other instance methods.


 

Employee.java

package com.sample.app.recrods;

public record Employee(int id, String name) {
	
	static {
		System.out.println("Employee record loaded");
	}

	private static int count = 0;

	public Employee {
		count++;
	}

	public static int empCount() {
		return count;
	}
	
	public String empDetails() {
		return this.toString();
	}

}

 

In the above snippet, empDetails is an instance method.

 

Can a record implement an interface?

Yes, a record can implement interfaces.

public record Employee(int id, String name) implements Serializable {

}

 

Can a record declare nested types?

A record can declare nested types, including nested records

 

What are the limitations on records?

a.   Since a Record extends java.lang.Record implicitly, a record can’t extend any class explicitly.

b.   Since a record is implicitly final, it can’t be abstract.

c.    The implicitly declared fields corresponding to the record components of a record class are final and moreover are not modifiable via reflection (doing so will throw IllegalAccessException).

d.   A record cannot declare native methods.

 

References

https://openjdk.org/jeps/384

https://openjdk.org/jeps/12


  

Previous                                                 Next                                                 Home