final (Java)
In the Java programming language, the final
keyword is used in several contexts to define an entity that can only be assigned once.
Once a final
variable has been assigned, it always contains the same value. If a final
variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object (this property of final
is called non-transitivity[1]). This applies also to arrays, because arrays are objects; if a final
variable holds a reference to an array, then the components of the array may be changed by operations on the array, but the variable will always refer to the same array.[2]
Final classes
A final class cannot be subclassed. Doing this can confer security and efficiency benefits, so many of the Java standard library classes are final, such as java.lang.System
and java.lang.String
.
Example:
public final class MyFinalClass {...}
public class ThisIsWrong extends MyFinalClass {...} // forbidden
Final methods
A final method cannot be overridden or hidden by subclasses.[3] This is used to prevent unexpected behavior from a subclass altering a method that may be crucial to the function or consistency of the class.[4]
Example:
public class Base
{
public void m1() {...}
public final void m2() {...}
public static void m3() {...}
public static final void m4() {...}
}
public class Derived extends Base
{
public void m1() {...} // OK, overriding Base#m1()
public void m2() {...} // forbidden
public static void m3() {...} // OK, hiding Base#m3()
public static void m4() {...} // forbidden
}
A common misconception is that declaring a method as final
improves efficiency by allowing the compiler to directly insert the method wherever it is called (see inline expansion). Because the method is loaded at runtime, compilers are unable to do this. Only the runtime environment and JIT compiler know exactly which classes have been loaded, and so only they are able to make decisions about when to inline, whether or not the method is final.[5]
Machine code compilers that generate directly executable, platform-specific machine code, are an exception. When using static linking, the compiler can safely assume that methods and variables computable at compile-time may be inlined.
Final variables
A final variable can only be initialized once, either via an initializer or an assignment statement. It does not need to be initialized at the point of declaration: this is called a "blank final" variable. A blank final instance variable of a class must be definitely assigned in every constructor of the class in which it is declared; similarly, a blank final static variable must be definitely assigned in a static initializer of the class in which it is declared; otherwise, a compile-time error occurs in both cases.[6] (Note: If the variable is a reference, this means that the variable cannot be re-bound to reference another object. But the object that it references is still mutable, if it was originally mutable.)
Unlike the value of a constant, the value of a final variable is not necessarily known at compile time. It is considered good practice to represent final constants in all uppercase, using underscore to separate words.[7]
Example:
public class Sphere {
// pi is a universal constant, about as constant as anything can be.
public static final double PI = 3.141592653589793;
public final double radius;
public final double xPos;
public final double yPos;
public final double zPos;
Sphere(double x, double y, double z, double r) {
radius = r;
xPos = x;
yPos = y;
zPos = z;
}
[...]
}
Any attempt to reassign radius
, xPos
, yPos
, or zPos
will result in a compile error. In fact, even if the constructor doesn't set a final variable, attempting to set it outside the constructor will result in a compilation error.
To illustrate that finality doesn't guarantee immutability: suppose we replace the three position variables with a single one:
public final Position pos;
where pos
is an object with three properties pos.x
, pos.y
and pos.z
. Then pos
cannot be assigned to, but the three properties can, unless they are final themselves.
Like full immutability, the use of final variables has great advantages, especially in optimization. For instance, Sphere
will probably have a function returning its volume; knowing that its radius is constant allows us to memoize the computed volume. If we have relatively few Sphere
s and we need their volumes very often, the performance gain might be substantial. Making the radius of a Sphere
final
informs developers and compilers that this sort of optimization is possible in all code that uses Sphere
s.
Though it appears to violate the final
principle, the following is a legal statement:
for (final SomeObject obj : someList) {
// do something with obj
}
Since the obj variable goes out of scope with each iteration of the loop, it is actually redeclared each iteration, allowing the same token (i.e. obj
) to be used to represent multiple variables.[8]
Although it also appears to break the intent of final variables, modifying the value from another class (by getting the reference via a public getter method and changing its value) does change the value of the referenced object. For instance, according to the semantics of the final keyword, the following unit test should output "68", while in fact it outputs "82" and throws an assertion exception:
package test;
import java.sql.Date;
public final class TestFinal {
private final String name = "Andrew";
@SuppressWarnings("deprecation")
private final Date dob = new Date(Date.parse("07/20/1968"));
public String getName() {
return name;
}
final public Date getDob() {
return dob;
}
}
package test;
import static org.junit.Assert.*;
import java.util.Date;
import org.junit.Test;
public class TestFinalTest {
@Test
public void test() {
}
@Test
public void changeDate() {
TestFinal testFinal = new TestFinal();
Date initialDate = testFinal.getDob();
initialDate.setYear(82);
Date finalDate = testFinal.getDob();
System.out.println(finalDate.getYear());
assert(finalDate.getYear() == 68);
}
}
The reason for this is that declaring a variable final only affects the reference, not the value of the variable, and Java always passes by value, which creates a different reference to the same value in the second class. That the value of the private field can be changed without a public setter implies that encapsulation is weak at best, and shouldn't be counted on to protect key values, even in combination with the final keyword.
Final and inner classes
When an anonymous inner class is defined within the body of a method, all variables declared final
in the scope of that method are accessible from within the inner class. For scalar values, once it has been assigned, the value of the final
variable cannot change. For object values, the reference cannot change. This allows the Java compiler to "capture" the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class's private copy persists in the class's own memory.
import javax.swing.*;
public class FooGUI {
public static void main(String[] args) {
//initialize GUI components
final JFrame jf = new JFrame("Hello world!"); //allows jf to be accessed from inner class body
jf.add(new JButton("Click me"));
// pack and make visible on the Event-Dispatch Thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jf.pack(); //this would be a compile-time error if jf were not final
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
});
}
}
Blank final
The blank final, which was introduced in Java 1.1, is a final variable whose declaration lacks an initializer.[9][10] A blank final can only be assigned once and must be unassigned when an assignment occurs. In order to do this, a Java compiler runs a flow analysis to ensure that, for every assignment to a blank final variable, the variable is definitely unassigned before the assignment; otherwise a compile-time error occurs.[11]
C/C++ analog of final variables
In C and C++, the analogous construct is the const
keyword. This differs substantially from final
in Java, most basically in being a type qualifier: const
is part of the type, not only part of the identifier (variable). This also means that the constancy of a value can be changed by casting (explicit type conversion), in this case known as "const casting". Nonetheless, casting away constness and then modifying the object results in undefined behavior.
Further, because C and C++ expose pointers and references directly, there is a distinction between whether the pointer itself is constant, and whether the data pointed to by the pointer is constant. Applying const
to a pointer itself, as in SomeClass * const ptr
, means that the contents being referenced can be modified, but the reference itself cannot (without casting). This usage results in behaviour which mimics the behaviour of a final
variable reference in Java. By contrast, when applying const to the referenced data only, as in const SomeClass * ptr
, the contents cannot be modified (without casting), but the reference itself can. Both the reference and the contents being referenced can be declared as const
.
In C++, const
is a soft guideline that programmers can easily override by type casting a const reference to a non-const reference. Java's final
is a strict rule such that it is impossible to compile code that directly breaks or bypasses the final restrictions. Using reflection, however, it is often possible to still modify final variables. This feature is mostly made use of when deserializing objects with final members.
References
- ^ Coblenz, Michael; Sunshine, Joshua; Aldrich, Jonathan; Myers, Brad; Weber, Sam; Shull, Forrest (14–22 May 2016). "Exploring Language Support for Immutability". The 38th International Conference on Software Engineering.
- ^ Java Language Specification #4.12.4
- ^ JLS 8.4.3.3. final Methods
- ^ Writing Final Classes and Methods
- ^ Java theory and practice: Is that your final answer?
- ^ Java Language Specification #8.3.1.2.
- ^ http://geosoft.no/development/javastyle.html
- ^ Pattis, Richard E. "More Java". Advanced Programming/Practicum 15–200. School of Computer Science Carnegie Mellon University. Retrieved 23 July 2010.
- ^ Flanagan, David (May 1997). "Chapter 5 Inner Classes and Other New Language Features:5.6 Other New Features of Java 1.1". Java in a Nutshell (2nd ed.). O'Reilly. ISBN 1-56592-262-X.
- ^ "Chapter 4. Types, Values, and Variables". The Java® Language Specification (Java SE 8 Edition). Oracle America, Inc. 2015. Retrieved 23 Feb 2015.
- ^ "Definite Assignment". The Java® Language Specification (Java SE 8 Edition). Oracle America, Inc. 2015. Retrieved 29 Oct 2016.