The compiler automatically generates the necessary declarations for a record class when it is declared according to the simplified syntax form, in particular, when the record body is empty.
- Each record component in the component list results in a private, final instance field in the record. In other words, these component fields pertain to objects of the record class, and their values constitute the state of a record. From the CD record class declaration, the following component fields are created: String artist, String title, int noOfTracks, Year year, and Genre genre. The component fields in the CD record class are equivalent to the instance field declarations in the CD_v0 class in Example 5.27.
Fields being final means that once a final field is initialized, its value cannot be changed. However, if a field refers to a mutable object, this object can be modified, but the reference value of the object assigned to the field cannot be changed. Record classes are thus said to be shallowly immutable.
The component fields in the CD record class have immutable values, as objects of String, Year, and Genre are immutable, as are primitive values. The CD record class is therefore immutable. (See also §6.7, p. 356, for a discussion on immutability.)
As we shall see, the order of the record components in the component list has implications in the record class.
- Each record component results in a public zero-argument get method (also known as a getter method). It is important to note that the get method has the same name and return type as the corresponding record component. This does not create any problem, as method names and field names are in different name spaces.
The compiler thus ensures that each record component has a corresponding private, final component field and a read-only public zero-argument get method that returns the value of this field.
The following public zero-argument get methods are created for the CD record class: String artist(), String title(), int noOfTracks(), Year year(), and Genre genre(), each having the name and the return type as declared by the corresponding record component. As expected, calling a get method returns the value of the corresponding component field. In Example 5.28, the get methods are called at (3) to obtain the values of individual component fields in a CD record. Although their names are different, the get methods of the CD record class are equivalent to the get methods of the CD_v0 class in Example 5.27.
- In order to initialize all component fields properly, the compiler automatically creates a public constructor—called the implicit canonical constructor—that has a parameter list that corresponds to the record components in name, type and order. A call to the canonical constructor in a new expression results in the argument values being assigned to the corresponding component fields in the same order. The implicit canonical constructor of a record class functions analogously to the default constructor in a normal class.
The implementation of the implicit canonical constructor for the CD record class is equivalent to the non-zero argument normal constructor implemented in the CD_v0 class in Example 5.27.
Creating records is no different from creating objects of a normal class. In Example 5.28, CD records are constructed by calling the implicit canonical constructor at (2) in the new expression. Note that the order of the specified values corresponds to that of the record components in the component list.
CD cd0 = new CD(“Jaav”, “Java Jive”, 8, Year.of(2017), Genre.POP);
…
- The compiler also creates an implementation of the toString() method that is overridden from the Object class. The toString() method creates a default text representation of a record that includes the name of the record class and a textual list with name-value pairs for each component field in the order specified in the component list.
In Example 5.28, the toString() method is explicitly called in the print statement at (4), resulting in the following output:
CD[artist=Jaav, title=Java Jive, noOfTracks=8, year=2017, genre=POP]
- An implementation of the equals() method (§14.2, p. 744) that is overridden from the Object class is also created to compare two CD records for equality, based on the values of corresponding component fields in the two records. The method compares the component fields in the same order as specified in the component list, and returns true if the values in corresponding fields all match—analogous to the implementation of the equals() method in the CD_v0 class in Example 5.27. Overriding of the equals() method is essential if records are to be used in collections (§15.12, p. 866).
In Example 5.28, two records cd0 and cdX are compared for equality with the equals() method, showing that the two records are equal, since they have the same state.
- Lastly, the compiler generates an implementation of the hashCode() method (§14.3, p. 753) that is overridden from the Object class. The method returns a hash code that is computed based on the values of the component fields, in the same order as specified in the component list—analogous to the implementation of the hashCode() method in the CD_v0 class in Example 5.27. Overriding of the hashCode() method is essential if records are to be used in sets and maps (§15.8, p. 830).
Restrictions on Record Declarations
A record class is implemented as an implicitly final direct subclass of the java.lang.Record abstract class that defines the default inherited behavior of such classes. In particular, the Record class defines abstract methods that override the equals(), hashCode(), and toString() methods of the Object class. This means that every record class must implement these methods either explicitly or implicitly. We have seen in Example 5.28 that the compiler provides the implementation of these methods automatically when any of these methods is not declared explicitly in the record class.
A record class cannot have an explicit extends clause to declare a direct superclass, not even its direct superclass Record. Being final also means that a record class cannot be declared abstract. However, a record class is seldom declared explicitly final. As such, a record class is solely defined by its state and cannot be extended by any subclass.
Note also that the name of a record component in the component list cannot be the same as the name of any of the methods in the Object class, except equals.
Leave a Reply