55

I was told that, I misunderstand effects of final. What are the effects of final keyword?

Here is short overview of what I think, I know:

Java final modifier (aka aggregation relation)

primitive variables: can be set only once. (memory and performance gain)
objects variables: may be modified, final applies to object reference.
fields: can be set only once.
methods: can't be overridden, hidden.
classes: can't be extended.
garbage collection: will force Java generational garbage collection mark-sweep to double sweep.

Can's and Cant's

  • Can make clone fail (this is both good and bad)
  • Can make immutable primitives aka const
  • Can make blank immutable - initialized at creation aka readonly
  • Can make objects shallowly immutable
  • Can make scope / visibility immutable
  • Can make method invocation overhead smaller (because it does not need virtual table)
  • Can make method arguments used as final (even if thy are not)
  • Can make objects threadsafe (if object is defined as final, it wont make method arguments final)
  • Can make mock tests (not that you could do anything about it - you can say bugs are intended)
  • Can't make friends (mutable with other friends and immutable for rest)
  • Can't make mutable that is changed to be immutable later (but can with factory pattern like fix)
  • Can't make array elements immutable aka deeply immutable
  • Can't make new instances of object (this is both good and bad)
  • Can't make serialization work

There are no alternatives to final, but there is wrapper + private and enums.

Margus
  • 19,694
  • 14
  • 55
  • 103
  • I would distill the answer to the language flaw of defaulting to mutable variables should not be fixed by the programmer. I would neither memorize nor even think about the "can" and "cannot" list. It's of zero practical value. Use final on instance variables where the benefit is obvious: the reference is shared. In block scoped variables keep it simple and free of clutter that really provides zero practical benefit, as the language designer's intended. In fact, where possible, please use "var" and ditch the redundant type declaration. – Rick O'Shea Sep 20 '22 at 19:49

2 Answers2

75

Answering each of your points in turn:

primitive variables: can be set only once. (memory and performance gain)

Yes, but no memory gain, and no performance gain. (Your supposed performance gain comes from setting only once ... not from final.)

objects variables: may be modified, final applies to object reference.

Yes. (However, this description miss the point that this is entirely consistent with the way that the rest of the Java language deals with the object / reference duality. For instance, when objects are passed as parameters and returned as results.)

fields: can be set only once.

The real answer is: same as for variables.

methods: can't be overridden, hidden.

Yes. But also note that what is going on here is that the final keyword is being used in a different syntactic context to mean something different to final for an field / variable.

classes: can't be extended.

Yes. But also see note above.

garbage collection: will force Java generational garbage collection mark-sweep to double sweep.

This is nonsense. The final keyword has no relevance whatsoever to garbage collection. You might be confusing final with finalization ... they are unrelated.

But even finalizers don't force an extra sweep. What happens is that an object that needs finalization is set on one side until the main GC finishes. The GC then runs the finalize method on the object and sets its flag ... and continues. The next time the GC runs, the object is treated as a normal object:

  • if it is reachable it is marked and copied
  • if it is not reachable it is not marked.

(Your characterization - "Java generational garbage collection mark-sweep" is garbled. A garbage collector can be either "mark-sweep" OR "generational" (a subclass of "copying"). It can't be both. Java normally uses generational collection, and only falls back to mark-sweep in emergencies; i.e. when running out of space or when a low pause collector cannot keep up.)

Can make clone fail (this is both good and bad)

I don't think so.

Can make immutable primitives aka const

Yes.

Can make blank immutable - initialized at creation aka readonly

Yes ... though I've never heard the term "blank immutable" used before.

Can make objects shallowly immutable

Object mutability is about whether observable state may change. As such, declaring attributes final may or may not make the object behave as immutable. Besides the notion of "shallowly immutable" is not well defined, not least because the notion of what "shallow" is cannot be mapped without deep knowledge of the class semantics.

(To be clear, the mutability of variables / fields is a well defined concept in the context of the JLS. It is just the concept of mutability of objects that is undefined from the perspective of the JLS.)

Can make scope / visibility immutable

Terminology error. Mutability is about object state. Visibility and scope are not.

Can make method invocation overhead smaller (because it does not need virtual table)

In practice, this is irrelevant. A modern JIT compiler does this optimization for non-final methods too, if they are not overridden by any class that the application actually uses. (Clever stuff happens ...)

Can make method arguments used as final (even if thy are not)

Huh? I cannot parse this sentence.

Can make objects threadsafe

In certain situations yes.

(if object is defined as final, it wont make method arguments final)

Yes, if you mean if class is final. Objects are not final.

Can make mock tests (not that you could do anything about it - you can say bugs are intended)

Doesn't parse.

Can't make friends (mutable with other friends and immutable for rest)

Java doesn't have "friends".

Can't make mutable that is changed to be immutable later (but can with factory pattern like fix)

Yes to the first, a final field can't be switched from mutable to immutable.

It is unclear what you mean by the second part. It is true that you can use a factory (or builder) pattern to construct immutable objects. However, if you use final for the object fields at no point will the object be mutable.

Alternatively, you can implement immutable objects that use non-final fields to represent immutable state, and you can design the API so that you can "flip a switch" to make a previously mutable object immutable from now onwards. But if you take this approach, you need to be a lot more careful with synchronization ... if your objects need to be thread-safe.

Can't make array elements immutable aka deeply immutable

Yes, but your terminology is broken; see comment above about "shallow mutability".

Can't make new instances of object (this is both good and bad)

No. There's nothing stopping you making a new instance of an object with final fields or a final class or final methods.

Can't make serialization work

No. Serialization works. (Granted, deserialization of final fields using a custom readObject method presents problems ... though you can work around them using reflection hacks.)

There are no alternatives to final,

Correct.

but there is wrapper + private

Yes, modulo that (strictly speaking) an unsynchronized getter for a non-final field may be non-thread-safe ... even if it is initialized during object construction and then never changed!

and enums.

Solves a different problem. And enums can be mutable.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 9
    Well, Java does have friends (quite a lot, in fact). It just does not have friends in the sense that C++ uses them ;-). – sleske Oct 25 '10 at 08:02
  • 3
    If by "Can make clone fail" he means, "Can make a deep clone difficult to impossible to implement" then that is true. Also, final can make custom serialization hard, although basic serialization works. – ILMTitan Oct 25 '10 at 14:26
  • the mutability of visible variables is used for anonymous classes. Only immutable (final) local variables are visible to an anonymous class. So the terminology could work. – josefx Oct 25 '10 at 16:32
  • 1
    @josefx - it is the notion of "deep mutability" that is broken. Mutability *per se* makes sense, provided that you don't talk about mutability of variables and mutability of objects in the same breath. – Stephen C Oct 25 '10 at 22:32
  • 1
    @ILMTitan - 1) The standard clone contract says that it is shallow. 2) deep clone should always be possible, assuming that you are free to implement your own private constructor in the class you are implementing `clone`. 3) Java clone, and especially deep clone is conceptually flawed, and experienced developers should avoid it. If you need copying, implement using a copy constructor or final methods, and with well-defined *application specific* semantics. – Stephen C Oct 25 '10 at 22:52
  • @Stephen C - 1) Object.clone() creates a shallow copy, but the Javadoc says it should be overridden to perform a deep one (It uses the phrase "Independent"). 2) The standard convention for clone is to operate on a copy returned by super.clone(). That way, the copy will always be of the correct type even when a subtype does not override clone. Don't use constructors in clone. 3) Agreed. – ILMTitan Oct 26 '10 at 14:22
  • I think when people say that "final" can improve performance, what they probably mean is that the compiler can take advantage of the knowledge that it's final to make optimizations that it would otherwise not be able to do. Frankly I have no idea if or to what extent any available Java compiler does this, but it's certainly conceivable. – Jay Jan 28 '11 at 21:29
  • @Jay - Hotspot compilers do optimize final methods to get rid of unnecessary virtual dispatching. In addition Sun Hotspot compilers examine the set of classes used in an application to identify non-final classes / methods that can be optimized in the same way. – Stephen C Jan 28 '11 at 22:45
  • @StephenC By using final I think there is a performance gain. From JLS8 : Correspondingly, compilers are allowed to keep the value of a final field cached in a register and not reload it from memory in situations where a non-final field would have to be reloaded. https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5 – tur1ng Sep 22 '18 at 15:34
  • @JAAAY - For final fields that is true. For local variables that is not true. The JIT compiler can determine that a variable is *effectively final* and then apply the same optimizations as for a variable that is declared `final`. – Stephen C Sep 23 '18 at 00:38
2

Final keyword is usually used to preserve immutability. To use final for classes or methods is to prevent linkages between methods from being broken. For example, suppose the implementation of some method of class X assumes that method M will behave in a certain way. Declaring X or M as final will prevent derived classes from redefining M in such a way as to cause X to behave incorrectly.

Katie
  • 452
  • 7
  • 18
  • Final declares immutability. It does not "preserve" it. Clearly immutable should have been the default but they went with simplicity and convenience. The best option is to use it on instance variables where it has practical benefits. Otherwise, obsessive use of final is just clutter with no practical benefit. – Rick O'Shea Sep 20 '22 at 19:54