Declaring immutable fields

Pokemon Go is a location-based augmented-reality game in which players use the mobile device's GPS capability to locate, capture, train, and make virtual creatures fight. This game had great success and popularized location-based and augmented-reality gaming. After its great success, imagine that we have to develop a Web Service that will be consumed by a similar game that makes virtual creatures battle.

We have to move to the world of virtual creatures. We will definitely have a VirtualCreature base class. Each specific type of virtual creature with unique characteristics that can participate in battles will be a subclass of VirtualCreature.

All the virtual creatures will have a name and they will be born in a specific year. The age is going to be extremely important for their performance in battles. Thus, our base class will have the name and birthYear fields that all the subclasses will inherit.

When we design classes, we want to make sure that all the necessary data is available to the methods that will operate on this data. For this reason, we encapsulate data. However, we just want relevant information to be visible to the users of our classes that will create instances, change the values of accessible fields, and call the available methods. We want to hide or protect some data that is just needed for internal use, that is, for our methods. We don't want to make accidental changes to sensitive data.

For example, when we create a new instance of any virtual creature, we can use both its name and birth year as two parameters for the constructor. The constructor initializes the values of two properties: name and birthYear. The following lines show a sample code that declares the VirtualCreature class. The code file for the sample is included in the java_9_oop_chapter_04_01 folder, in the example04_01.java file.

class VirtualCreature {
    String name;
    int birthYear;

    VirtualCreature(String name, int birthYear) {
        this.name = name;
        this.birthYear = birthYear;
    }
}

The next lines create two instances that initialize the values of the two fields and then use the System.out.printf method to display their values in JShell. The code file for the sample is included in the java_9_oop_chapter_04_01 folder, in the example04_01.java file.

VirtualCreature beedrill = new VirtualCreature("Beedril", 2014);
System.out.printf("%s\n", beedrill.name);
System.out.printf("%d\n", beedrill.birthYear);
VirtualCreature krabby = new VirtualCreature("Krabby", 2012);
System.out.printf("%s\n", krabby.name);
System.out.printf("%d\n", krabby.birthYear);

The following screenshot shows the results of the declaration of the class and the execution of the previous lines in JShell:

We don't want a user of our VirtualCreature class to be able to change the name for a virtual creature after an instance is initialized because the name is not supposed to change. Well, some people change their names but this never happens with virtual creatures. There is a simple way to achieve this goal in our previously declared class. We can add the final keyword before the type (String) to define an immutable name field of type String. We can also add the final keyword before the type (int) when we define the birthYear field because the birth year will never change after we initialize a virtual creature instance.

The following lines show the new code that declares the VirtualCreature class with two immutable instance fields: name and birthYear. Note that the constructor code doesn't need to be changed, and it is possible to initialize the two immutable instance fields with the same code. The code file for the sample is included in the java_9_oop_chapter_04_01 folder, in the example04_02.java file.

class VirtualCreature {
 final String name;
 final int birthYear;

    VirtualCreature(String name, int birthYear) {
        this.name = name;
        this.birthYear = birthYear;
    }
}
Note

Immutable instance fields are also known as non-mutating instance fields.

The next lines create an instance that initializes the values of the two immutable instance fields and then use the System.out.printf method to display their values in JShell. The code file for the sample is included in the java_9_oop_chapter_04_01 folder, in the example04_02.java file.

VirtualCreature squirtle = new VirtualCreature("Squirtle", 2014);
System.out.printf("%s\n", squirtle.name);
System.out.printf("%d\n", squirtle.birthYear);

The next two lines of code try to assign a new value to the name and birthYear immutable instance fields. The code file for the sample is included in the java_9_oop_chapter_04_01 folder, in the example04_03.java file.

squirtle.name = "Tentacruel";
squirtle.birthYear = 2017;

The two lines will fail to do so because Java doesn't allow us to assign a value to a field declared with the final modifier that transforms it into an immutable field. The next screenshot shows the errors displayed in JShell after each line that tries to set a new value to the immutable fields:

Tip

When we use the final keyword to declare an instance field, we can initialize the field but it becomes immutable, that is, a constant, after its initialization.