Java Record Classes

By Arvind Rai, July 30, 2021
This page will walk through Java record classes. Let us understand it step-by-step.
1. Java record class is a special kind of Java class that serves as a simple data carrier.
2. The record class was introduced in Java 14 as preview feature and previewed again in Java 15. The record class has been adopted as permanent language feature in Java 16 (JEP 395).
3. The record class fields are final and their appropriate accessors are created automatically.
4. The record class constructor, equals, hashCode, and toString methods are created automatically.
5. The record class is created using record keyword.
Find a sample record class.
record Employee(String name, int age, String city) { } 
The constructor of Employee record class will accept three arguments that are 'name', 'age' and 'city'. The final fields of Employee record class will be of same name that are 'name', 'age' and 'city' and its accessor methods will be 'name()', 'age()' and 'city()'. All these fields and methods will be created automatically. We can use this record class in our code as following.
Employee emp = new Employee("Mahesh", 22, "Varanasi");
System.out.println(emp.name()); //Mahesh
System.out.println(emp.age()); //22
System.out.println(emp.city()); //Varanasi 
Now let us discuss creating and using record class in detail.

1. Creating Record Class

A record class is created using record keyword. We are creating a record class Item here.
record Item(int id, String name) { } 
We have passed two parameters that are id and name in above record class. The Item class will have two final fields that are id and name. The accessors for Item class will be created automatically and that are id() and name() methods. The constructor, equals, hashCode, and toString methods will also be created automatically for Item record class. We can instantiate our record class Item as following.
Item item = new Item(10, "Item1"); 
The fields of Item instance can be accessed by calling item.id() and item.name().
Find the complete code.
MyApp.java
package com.concretepage;
record Item(int id, String name) { }
public class MyApp {
  public static void main(String[] args) {
     Item item = new Item(10, "Item1");
     System.out.println(item.id() +", " + item.name());
  }
} 
Output
10, Item1 

2. Creating Java Class equivalent to Record Class

A record class avoids writing lots of lines of code. Suppose we have a record class as following.
record Item(int id, String name) { } 
To create the equivalent class without using record keyword, we need to write following boilerplate code.
Item.java
package com.concretepage;
import java.util.Objects;
public final class Item {
  private final int id;
  private final String name;
  public Item(int id, String name) {
	this.id = id;
	this.name = name;
  }
  int id() {
	return this.id;
  }
  String name() {
	return this.name;
  }
  @Override
  public int hashCode() {
	return Objects.hash(id, name);
  }
  @Override
  public boolean equals(Object obj) {
	if (this == obj) {
	  return true;
	} else if (obj instanceof Item other) {
	  return Objects.equals(id, other.id) && Objects.equals(name, other.name);
	} else {
	  return false;
	}
  }
  @Override
  public String toString() {
	return "Item[id=" + id + ", name=" + name + "]";
  }
} 
In this way we can understand the following points.
a. A record class is a final class. It means we cannot inherit a record class.
b. All the fields of record class are final.
c. Accessor methods of record class are named as field names.
d. The constructor, hashCode, equals, and toString methods are created by default in a record class.

3. Compact Constructor

We can declare record class with compact constructor. Compact constructor can be used to validate arguments or for some other processing.
Example:
package com.concretepage;
record Item (int id, String name) {
  public Item {
	if (id < 100 || name.isEmpty()) {
	  throw new IllegalArgumentException("Invalid data");
	}
  }
}
public class MyApp {
  public static void main(String[] args) {
     Item item1 = new Item(101, "Item1");
     System.out.println(item1);
     Item item2 = new Item(50, "Item2");
     System.out.println(item2);     
  }
} 
Output
Item[id=101, name=Item1]
Exception in thread "main" java.lang.IllegalArgumentException: Invalid data 

4. Explicit Declaration of Accessors

We can explicitly declare methods that correspond to our record class components. We need to ensure that they have the same characteristics as implicitly derived accessors, such as it should be public and have same return type as the corresponding record class component. In the same way, while implementing hashCode, equals, and toString, we should ensure that they have the same characteristics and behavior as those in the java.lang.Record class because this is the super class of all record classes.
Find the example to explicitly declare accessor method.
package com.concretepage;
record Item (int id, String name) {
  public String name() {
	System.out.println("Item name: " + name);
	return name;
  }
}
public class MyApp {
  public static void main(String[] args) {
     Item item = new Item(101, "Item1");
     System.out.println(item.name());
  }
} 
Output
Item name: Item1
Item1 

5. Explicit Declaration of static Fields, static Initializers, and static Methods

We can declare static fields, static initializers, and static methods in a record class and they behave in the same way as in normal class. Find the sample example.
package com.concretepage;
record Team(int id, String name, String tech) {
  static String javaTech;
  static String angularTech;
  static {
	javaTech = "Java";
	angularTech = "Angular";
  }
  public static Team createJavaTeam(int id, String name) {
	return new Team(id, name, javaTech);
  }
  public static Team createAngularTeam(int id, String name) {
	return new Team(id, name, angularTech);
  }
}
public class MyApp {
  public static void main(String[] args) {
	Team team1 = Team.createJavaTeam(101, "Team A");
	System.out.println(team1);
	Team team2 = Team.createAngularTeam(201, "Team B");
	System.out.println(team2);
  }
} 
Output
Team[id=101, name=Team A, tech=Java]
Team[id=201, name=Team B, tech=Angular] 

6. Explicit Declaration of Instance Methods

We can declare explicit instance methods in a record class but we cannot declare instance variables (non-static fields) or instance initializers in a record class.
Find the example to declare an instance method in a record class.
package com.concretepage;
record Rectangle(int length, int width) {
  public int getArea() {
	return length * width;
  }
}
public class MyApp {
  public static void main(String[] args) {
	Rectangle r = new Rectangle(10, 5);
	int area = r.getArea();	
	System.out.println(area);
  }
} 
Output is 50.

7. Nested Record Class

We can declare nested record classes within a record class. Nested record classes are implicitly static.
package com.concretepage;
record Rectangle(int length, int width) {
  record BigRectangle(int num) {
	public BigRectangle {
	  num = num * 2;
	}
  }
  public Rectangle createBigRectangle(int num) {
	BigRectangle b = new BigRectangle(num);
	int l = length + b.num();
	int w = width + b.num();
	return new Rectangle(l, w);
  }
}
public class MyApp {
  public static void main(String[] args) {
	Rectangle r = new Rectangle(10, 5);
	Rectangle br = r.createBigRectangle(3);
	System.out.println(br);
  }
} 
Output
Rectangle[length=16, width=11] 

8. Local Record Class

We can create local record class within the body of a method similar to a local class. Find the example.
package com.concretepage;
public class MyApp {
  public static void main(String[] args) {
	record Item(int id, String name) {}
	Item item = new Item(10, "Item1");
	System.out.println(item);
	MyApp ob = new MyApp();
	int area = ob.calculateArea(5);
	System.out.println(area);
  }
  int calculateArea(int size) {
	record Rectangle(int length, int width) {}
	Rectangle r = new Rectangle(size * 2, size);
	return r.length() * r.width();
  }
} 
Output
Item[id=10, name=Item1]
50 

9. Additional Features of Record Classes

1. We can create a generic record class.
record Rectangle<C extends Shape> (C length, C width) { } 
2. We can declare record classes that implement one or more interfaces.
record Employee(int id, String name) implements Person { } 
3. We can annotate a record class and its individual components.
record Rectangle(
    @GreaterThanZero int length,
    @GreaterThanZero int width) { } 
4. Prior to Java 16, we could not declare an explicitly or implicitly static member in an inner class unless that member is a constant variable. Since Java 16, we can do it and hence an inner class can declare a nested record class. A nested record class is implicitly static.
5. We can serialize and deserialize instances of record classes but we cannot customize its process by using writeObject, readObject, readObjectNoData, writeExternal, or readExternal methods.
6. In Java 16, the java.lang.Class introduces two methods related to abstract Record class that are given as below.
(a)
RecordComponent[] getRecordComponents() 
This method returns an array of RecordComponent objects representing all the record components of this record class.
(b)
boolean isRecord() 
This method returns true if and only if this class is a record class.

10. Creating and Importing a Custom Class with Name "Record"

The super class of all record classes is java.lang.Record class that is an abstract class. All the classes of java.lang package are imported by default in a class. Now let us understand how to import our own class named as Record in a class.
Suppose we create a class named as Record in our package com.cp as below.
package com.cp;
public class Record {
  private String name;
  public Record(String name) {
	this.name = name;
  }
  public String getRecordName() {
	return name;
  }
} 
To use above Record class, we cannot import it as following.
import com.cp.*; 
It will give error as
error: reference to Record is ambiguous 
The error is because Record class is also imported from java.lang.* by default.
To avoid this ambiguity, we need to use fully qualified name of Record class as given below.
import com.cp.Record; 
Find the complete code to use it.
package com.concretepage;
import com.cp.Record;
public class MyApp {
  public static void main(String[] args) {
	Record r = new Record("MyRecord");
	System.out.println(r.getRecordName());
  }
} 

11. References

Record Classes
JEP 395: Records
POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us