Showing posts with label java14. Show all posts
Showing posts with label java14. Show all posts

Sunday, 23 May 2021

Java14: Record type

Record type is a preview feature in Java14, provides a compact syntax for classes which are transparent holders for shallowly immutable data.

 

For example, prior to java14, to define an immutable Employee class, you should write following code.

 

Employee.java

public final class Employee {

	private final Integer id;
	private final String firstName;
	private final String lastName;
	private final int age;

	public Employee(Integer id, String firstName, String lastName, int age) {
		super();
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.age = age;
	}

	public Integer getId() {
		return id;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public int getAge() {
		return age;
	}

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

}

With the record type, you can define an immutable Employee class in a compact form like below.

 

EmployeeRecord.java

public record EmployeeRecord(Integer id, String firstName, String lastName, int age) {

}


Record has a name and state description. State description declares the components of the record. In the above example,

a.   Name is EmployeeRecord and

b.   State description is (Integer id, String firstName, String lastName, int age)

 

You can access the properties of EmployeeRecord using method syntax like id(), firstName(), lastName(), age().

 

RecordDemo.java

public class RecordDemo {
	public static void main(String args[]) {
		EmployeeRecord emp1 = new EmployeeRecord(1, "Krishna", "Gurram", 32);
		
		System.out.println("id : " + emp1.id());
		System.out.println("firstName : " + emp1.firstName());
		System.out.println("lastName : " + emp1.lastName());
		System.out.println("age : " + emp1.age());
		System.out.println("emp : " + emp1);
	}

}


Output

id : 1
firstName : Krishna
lastName : Gurram
age : 32
emp : EmployeeRecord[id=1, firstName=Krishna, lastName=Gurram, age=32]


What is happening under the hood when you define a record?

a.   A private final field for each component of the state description;

b.   A public read accessor method for each component of the state description, with the same name and type as the component;

c.    A public constructor, whose signature is the same as the state description, which initializes each field from the corresponding argument;

d.   Implementations of equals and hashCode that say two records are equal if they are of the same type and contain the same state; and

e.   An implementation of toString that includes the string representation of all the record components, with their names.

 

Can I add additional fields to the record?

Yes, you can add an additional field to the record. But the additional field must be static.

 

Can I add methods to the record?

Yes, you can add both instance and static methods to the record. An instance method can access the internal state of the record fields.

 

EmployeeRecord.java

public record EmployeeRecord(Integer id, String firstName, String lastName, int age) {

	private static final String ORG_NAME = "ABC CORP";

	public static String getOrgName(){
		return ORG_NAME;
	}

	public String fullName(){
		return firstName + "," + lastName;
	}
}


RecordDemo.java

public class RecordDemo {
	public static void main(String args[]) {
		EmployeeRecord emp1 = new EmployeeRecord(1, "Krishna", "Gurram", 32);
		
		System.out.println("id : " + emp1.id());
		System.out.println("firstName : " + emp1.firstName());
		System.out.println("lastName : " + emp1.lastName());
		System.out.println("age : " + emp1.age());
		System.out.println("emp : " + emp1);

		System.out.println("\nOrganization name : " + EmployeeRecord.getOrgName());
		System.out.println("Full name : " + emp1.fullName());
	}

}


Output

id : 1
firstName : Krishna
lastName : Gurram
age : 32
emp : EmployeeRecord[id=1, firstName=Krishna, lastName=Gurram, age=32]

Organization name : ABC CORP
Full name : Krishna,Gurram


Can I explicitly define a constructor?

Java provides a special consideration is provided for explicitly declaring the canonical constructor (the one whose signature matches the record's state description). The constructor may be declared without a formal parameter list (in this case, it is assumed identical to the state description).

 

Syntax

public RecordName{}

 

EmployeeRecord.java

public record EmployeeRecord(Integer id, String firstName, String lastName, int age) {

	private static final String ORG_NAME = "ABC CORP";

	public EmployeeRecord{
		if(firstName == null){
			throw new IllegalArgumentException("firstName can't be null");
		}

		if(lastName == null){
			throw new IllegalArgumentException("lastName can't be null");
		}

		if(age <= 18){
			throw new IllegalArgumentException("Age must be > 18");
		}
	}

	public static String getOrgName(){
		return ORG_NAME;
	}

	public String fullName(){
		return firstName + "," + lastName;
	}
}


Can I define toString, hashCode and equals methods?

Yes, you can do. For example, below snippet defines toString() method explicitly.

 

EmployeeRecord.java

public record EmployeeRecord(Integer id, String firstName, String lastName, int age) {

	private static final String ORG_NAME = "ABC CORP";

	public EmployeeRecord{
		if(firstName == null){
			throw new IllegalArgumentException("firstName can't be null");
		}

		if(lastName == null){
			throw new IllegalArgumentException("lastName can't be null");
		}

		if(age <= 18){
			throw new IllegalArgumentException("Age must be > 18");
		}
	}

	public static String getOrgName(){
		return ORG_NAME;
	}

	public String fullName(){
		return firstName + "," + lastName;
	}

	public String toString(){
		StringBuilder builder  = new StringBuilder();
		builder.append("id : " + id).append("\n");
		builder.append("firstName : " + firstName).append("\n");
		builder.append("lastName : " + lastName).append("\n");
		builder.append("age : " + age).append("\n");
		return builder.toString();
	}
}

RecordDemo1.java

public class RecordDemo {
	public static void main(String args[]) {
		EmployeeRecord emp1 = new EmployeeRecord(1, "Krishna", "Gurram", 32);
		
		System.out.println("emp details :\n" + emp1);
	}

}

Output

emp details :
id : 1
firstName : Krishna
lastName : Gurram
age : 32

Reference

https://openjdk.java.net/jeps/359

  

Previous                                                    Next                                                    Home

Java14: Helpful NullPointerExceptions

Most of the Java developers encounter NullPointerExceptions while working with Java applications. Java14 enhances the usability of NullPointerException generated by the JVM by describing precisely which variable was null.

 

let’s see it with an example.

 

NullPointerDemo.java

public class NullPointerDemo {
	public static void main(String args[]) {
		
		String str = null;
		System.out.println("Length of str is "+ str.length());
		
	}
}

 

When you run above program on jdk prior to 14, you will get following output.

Exception in thread "main" java.lang.NullPointerException
	at NullPointerDemo.main(NullPointerDemo.java:5)

 

With the above output, you don’t know what is happened exactly, you need to go to the file NullPointerDemo.java and check the line number 5.

 

But when you ran same application in Java14, you will get following error message.

 

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
	at NullPointerDemo.main(NullPointerDemo.java:5)

 

As you see, from the above message, we can clearly understand that null pointer exception occurred on a variable of type string while calling the length() method.

 

Let’s see one more complex example.

 

Address.java

public class Address {
	private String city;
	private String street;
	private String country;

	public Address(String city, String street, String country) {
		super();
		this.city = city;
		this.street = street;
		this.country = country;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getStreet() {
		return street;
	}

	public void setStreet(String street) {
		this.street = street;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

}

 

Employee.java

public class Employee {

	private Integer id;
	private String name;
	private Address address;

	public Employee(Integer id, String name, Address address) {
		super();
		this.id = id;
		this.name = name;
		this.address = address;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

}

 

NullPointerDemo1.java

public class NullPointerDemo1 {

	private static void printStrInUpperCase(String str) {
		try {
			System.out.println(str.toUpperCase());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private static void printEmployee(Employee emp) {
		printStrInUpperCase(emp.getName());
		printStrInUpperCase(emp.getAddress().getCity());
		printStrInUpperCase(emp.getAddress().getStreet());
		printStrInUpperCase(emp.getAddress().getCountry());

	}

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

		Employee emp2 = new Employee(2, null, null);

		printEmployee(emp1);
		printEmployee(emp2);

	}
}

 

Output prior to Java14

KRISHNA
BANGALORE
java.lang.NullPointerException
	at NullPointerDemo1.printStrInUpperCase(NullPointerDemo1.java:5)
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:15)
	at NullPointerDemo1.main(NullPointerDemo1.java:26)
INDIA
java.lang.NullPointerException
	at NullPointerDemo1.printStrInUpperCase(NullPointerDemo1.java:5)
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:13)
	at NullPointerDemo1.main(NullPointerDemo1.java:27)
Exception in thread "main" java.lang.NullPointerException
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:14)
	at NullPointerDemo1.main(NullPointerDemo1.java:27)

 

Output in Java14

$java NullPointerDemo1
KRISHNA
BANGALORE
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "<parameter1>" is null
	at NullPointerDemo1.printStrInUpperCase(NullPointerDemo1.java:5)
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:15)
	at NullPointerDemo1.main(NullPointerDemo1.java:26)
INDIA
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "<parameter1>" is null
	at NullPointerDemo1.printStrInUpperCase(NullPointerDemo1.java:5)
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:13)
	at NullPointerDemo1.main(NullPointerDemo1.java:27)
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Address.getCity()" because the return value of "Employee.getAddress()" is null
	at NullPointerDemo1.printEmployee(NullPointerDemo1.java:14)
	at NullPointerDemo1.main(NullPointerDemo1.java:27)

 

Helpful NullpointerExceptions feature offers

a.   helpful information to developers and support staff about the premature termination of a program.

b.   Improve program understanding by more clearly associating a dynamic exception with static program code.

c.    Reduce the confusion and concern that new developers often have about NullPointerExceptions.

 

Reference

https://openjdk.java.net/jeps/358

 

 

 

Previous                                                    Next                                                    Home