DuyHai's Java Blog

Final variable in Java

Advertisements

This short article introduces the definition and usage of final variables in Java and some interesting use cases.

I Definition

A variable is said “final” when, after its declaration, its reference cannot be changed.

The below code will raise error during compilation because we attempt to modify a variable declared as final.


final Object finalObj = new Object();
...
finalObj = new Object(); // Compilation error

 

II Usage

The final keyword can be used in 3 situations:

The first case is quite straightforward, no much to say.

For a class attribute declaration, the final variable should either:


public class Test
{
	// Immediate initialization
	final String finalVar = "test";
}

public class Test
{
	final String finalVar;

	public Test(String input)
	{
		this.finalVar="firstConstructor";
	}

	public Test()
	{
		this.finalVar="defaultConstructor";
	}
}

Any subsequent attempt to assign a new value to finalVar will result in compilation error.

The last use case of final variable relates to method argument:


public class Test
{
	...
	public void doSomething(final String value)
	{
		...
		// Compilation error, forbidden
		value = "anotherString";
		...
	}
}

 

III Final vs immutable

There are some false beliefs among developers, stating that a final variable cannot be modified. It is right and wrong at the same time, depending on what we mean by “being modified“.

Let’s have a look at the definition of “final“: …after its declaration, its reference cannot be changed

The definition clearly states that the reference of a final variable cannot be changed after its initialization. Nothing is said about its internal state. Especially if the object is mutable, its internal state can be modified even though the variable is declared “final“.


public class Test
{
	public void addToList(final List<String> items)
	{
		items.add("dummy");
	}
}

...
	List<String> items = new ArrayList<String>();
	items.add("singleton");

	System.out.println("items size before == " + items.size());

	Test testObject = new Test();
	testObject.addToList(items);

	System.out.println("items size after == " + items.size());
...

The output displays:


items size before == 1
items size after == 2

Indeed, mutability and final are two distinct notions in Java. You can have a final mutable variable as well as final immutable variable.

You can have more details about immutability in my previous article Java immutability

 

IV Pass by reference/copy of reference

I’ve been told in the past to put the final modifier on all method/constructor arguments. The reason of this practice was, supposedly, to avoid modifying the reference of the object passed in argument.

Though I quite agree that it could be a good coding practice, it is absolutely not necessary. Even if you omit the final keyword for your method arguments, you still can’t change the reference of the source object.

Let’s see a concrete example:


public class Test
{
	public void addToList(List<String> items)
	{
		items = new ArrayList<String>();
		items.add("one");
		items.add("two");
		items.add("three");

		System.out.println("items size inside = " + items.size());
	}
}

...
	List<String> items = new ArrayList<String>();
	items.add("singleton");

	System.out.println("items size before == " + items.size());

	Test testObject = new Test();
	testObject.addToList(items);

	System.out.println("items size after == " + items.size());
...

Above, at line 5, we assign a new ArrayList instance to the items argument of the addToList() method. But with no surprise, the output is:


items size before == 1
items size inside == 3
items size after == 1

Indeed, as I showed in my post about Java passed by reference/value, the items argument of the addToList() method is a pointer pointing to the original list reference. Assigning this pointer to a new list reference inside addToList() doesn’t affect the original pointer declared at line 15.

 

V Final variable and anonymous classes

One usage of the final keyword relates to anonymous classes and the capture of local variables. According to the official documentation, an anonymous class can access to all variable of the enclosing class, provided that they are declared as “final”.


public interface Listener
{
	void onAction();

}


...
...
	public Listener getListener()
	{
		final List<String> list = new ArrayList<String>();

		Listener listener = new Listener()
		{

			public void onAction()
			{
				list.add("listener called");
			}
		};

		list.add("start");
		listener.onAction();
		list.add("end");
	}
...

Again, marking the local variable as final does not mean that the variable itself is immutable. The above code example clearly proves it.

Why do we need to declare a variable “final” for the anonymous class ? There is a good reason for that, as per Java language design.

Since “closure” does not exist in Java, as soon as the execution flow exits a method, all local variable references, which are stored on the call stack, are removed. The consequence is that when a method exits, all local variables declared inside this method no longer exist.

It is possible that in some cases, a method returned value is an anonymous class. If this anonymous class is using a local variable declared inside the method, on method exit, weird behaviors will occur.

To prevent such issue, the compilator forces the local variable to be declared as final if used in an anonymous class. Declaring a variable “final” will result in moving it from the call stack to a pool of “constants” so the variable still exists on method exit.

 
 

Advertisements