3

One of the things you see in numerous places in the standard java library is final classes. It is claimed that this is for immutability which I understand...to an extent.

Suppose you have a class:

final public class ImmutableTest {
    private String value;
    public ImmutableTest(String value) {
        this.value = value;
    }
    public String getValue() {
        return this.value;
    }
}

You can say that the value is immutable because it can't be adapted after creation. However this limits usability of the class as no one can extend it (for example to use as a drop-in for a bad interfaceless design...).

Now suppose you have this class:

public class ImmutableTest {
    private String value;
    public ImmutableTest(String value) {
        this.value = value;
    }
    final public String getValue() {
        return this.value;
    }
}

The "value" is still immutable but now at least people can extend the class. Someone once interjected: but if you extend it, the extended class is not guaranteed to be immutable!

While this is true, I think it is immaterial to the class itself.

If user A uses my ImmutableTest class everywhere in his code, he only has access to the "value" anyway which remains immutable. Unless he does explicit casting (in other words: he's aware that he's trying to access other stuff) he can't access a "mutable" part of the actual instance which means from the developer's point of view the class is as immutable as it should be, even if the actual instance has other mutable variables.

So in short: my opinion is that final classes are superfluous as final methods are all you need. I would love to hear arguments to the contrary though!

UPDATE

To clarify: I'm not claiming that adding "final" to a class or its methods makes it immutable.

Suppose you have a class that is designed to be immutable but you do not make the methods or class final, a subclass can of course make it mutable again. This argument is used as to why some classes in the standard library are final. My opinion is that it should've been enough to finalize the accessor methods to the immutable part and leave the class non-final.

This may leave the window open to the child objects adding mutable fields but the original set of fields would remain immutable hence satisfying the "immutable" requirement for the initial set of methods.

UPDATE2

The conclusion being that final classes have no real purpose except perhaps brevity which in itself is not enough of a reason to lose extensibility in my opinion.

nablex
  • 655
  • 1
  • 5
  • 14
  • Well, one reason would be readability and brevity. If your example class had in fact multiple private values with multiple "getters", without the class-level "final" you'd have to copy paste it on the declaration of each method. Also, if you intended to make the class immutable (rather than just making some of its methods final), its easier to convey that through one word at the very top, rather than multiple instances of that word peppered around in the class body. – Shivan Dragon Oct 15 '13 at 11:47
  • 1
    You're throwing two different concepts togethers that are only loosely related. – Pieter B Oct 15 '13 at 12:29
  • @PieterB http://docs.scala-lang.org/style/naming-conventions.html#special_note_on_brevity – Shivan Dragon Oct 15 '13 at 13:38
  • @PieterB it is not meant as a comment on finality in general, it is meant as a comment on finality with regards to immutability. In that respect they are very much related if guarantees are to be made by the class designer. – nablex Oct 15 '13 at 13:39
  • One more point to make is that inherited methods (like those from `Object` such as `hashCode`, `equals`, and `toString` _also_ need to be made `final` if the class itself is not made `final`. – cdeszaq Dec 13 '17 at 18:04

4 Answers4

9

Declaring immutable class final saves programmer from the need to repeat declaring final in each and every method declaration, over and over and over again.

In classes like java.lang.String, having over 60 methods, this is substantial save, as well as important guarantee that necessary modifier won't be omitted by mistake.

When object is intended to be immutable, mistakes to declare final method may be hard to detect, because there is no reliable way to tell whether programmer omitted final modifier intentionally or by mistake.

final classes have no real purpose except perhaps brevity

Besides brevity and helping to avoid mistakes, a strong benefit of declaring immutable class final is that this makes programmer's intent explicit, unambiguously communicating to API users that none of class methods are intended for override.

Alternative way, that is, declaring all methods final, lacks this important "feature", as it leaves users of the API in the indecisive state, whether API designers intended to only protect some methods from overloading, and it only accidentally turned out that all of them got final, or there was a design decision to cover all the class.

my opinion is that final classes are superfluous as final methods are all you need

Given above, having final modifier for class doesn't look superfluous to me.

It is worth noting that Sun/Oracle Java tutorial presents final classes as a matter of usefulness and not as that of convenience. If final classes purpose was mere brevity / syntactic sugar, one would expect tutorial to explain these as convenience.

...you can also declare an entire class final. A class that is declared final cannot be subclassed. This is particularly useful, for example, when creating an immutable class like the String class.

gnat
  • 21,442
  • 29
  • 112
  • 288
  • 1
    While what you say is true, the same can be said of a lot of language constructs. Developer errors will always occur, adding a shortcut to avoid having to think about a problem is how dynamic typing was invented :) – nablex Oct 15 '13 at 12:33
  • How do we know they didn't "accidently" declare the class `final`? – user949300 Apr 11 '18 at 14:12
  • As for "saving the need to repeat final" a skillion times, sorry, repetition in Java, for better or worse, is a feature. How many skillions of times must you write `private RepeatMe foo = new RepeatMe();`? – user949300 Apr 11 '18 at 14:18
  • 2
    I can't make sense of this question sorry. I'm talking about uncertainty when there's a mix of final and non-final methods within the class. Final modifier at class level is a whole different game, it is either present or absent: there's no ambiguity (side note with Java 10 repetitive example no longer holds, it's `private var foo = new RepeatMe();`) – gnat Apr 11 '18 at 14:24
8

Classes are not mutable or immutable, mutability usually refers to the state of objects at runtime.

Final classes, methods and members have different goals.

An final class cannot be extended by other classes. Depending on your design, this may be a desirable design property of this class. It does not mean that all members of this class are immutable by default! Logical immutability can be a side effect as in your example. If a class is final and all of its fields are as well, the object is strongly immutable.

Final methods mean that the method cannot be overridden in a subclass. This can be for example useful with the template method pattern where you do not want clients to override the template method instead of the hooks. This, again, has nothing to do with immutable state because it does not directly relate to a member being immutable.

Final fields is where immutablity comes really into play. If you placed the final keyword in front of value like private final String value;, you would make sure that a value could not be overriden no matter who extended what.

ftr
  • 1,601
  • 1
  • 14
  • 19
  • I meant to say that the argument is used that the entire class is final because its fields have to be immutable, not that the class itself is "immutable" as it of course refers to its fields. – nablex Oct 15 '13 at 11:47
  • 1
    Well, if field immutability is the goal, making the fields `final` is the way to go. – ftr Oct 15 '13 at 11:53
  • Making a field final is not good enough if the getter isn't final. A child class can simply override the getter and use some other mutable value – nablex Oct 15 '13 at 12:00
  • 2
    The getter has nothing to do with the immutability of `value`, though. But if you want to make `value` immutable and prohibit overriding of the getter, you can just make both the field and the getter `final`. Overriding a getter of an immutable field has a smelly feeling to it anyway. – ftr Oct 15 '13 at 12:07
  • The immutability of the private variable "value" is wasted without protecting the getter. – nablex Oct 15 '13 at 12:17
  • I disagree. The private variable `value` is guaranteed to be immutable if it is `final`. If you override the getter in a subclass with some other value, that may be a problem, but does have no bearing on the state of the field itself. – ftr Oct 15 '13 at 12:33
  • This is rather pointless. You know very well that any external call to a private class variable is going to go through the getter hence the finality is wasted if you don't secure the getter, especially considering that the topic at hand is immutability. – nablex Oct 15 '13 at 13:38
  • let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/11069/discussion-between-ftr-and-user1109519) – ftr Oct 15 '13 at 15:16
-1

State is defined by the fields in a class and so making them immutable(using final and private) should work for primitive type variables. But in case of variables containing a reference to some another object, say x, this x should be immutable too. While publishing the x from getter, we need to ensure that we are not compromising with x's immutability. For example:

class A
{
final int i;

final HashMap<int, B> map;

public A(int i,Map map){
this.i=i;
this.map=map;
}
public int getI(){
return i;
}
public int getJ(int index){
return map.get(index).getJ();
}

class B{
 int j;
public int getJ()
{
return j;
}

public void setJ(int j)
{
this.j=j;
}
}

....

From above code snippet, map is immutable since its final and the map is never published to outer world.

But what if some other class say C, extends A and then overrides getJ(int index) method?

To prevent it, we need to declare getJ(int index) method final. Now there can be many such methods in a class and checking whether each of them should be final can be error-prone. So we declare the class final and let other knows that none of its methods and variables are bounded to change and so overriding it is not possible.

We can declare map as private and final both , so its not accessible to child classes, but then again there can be some other mutable fields/variables in A or B which can be used and published by child class C.

  • Actually, your class `A` is mutable because you did not copy the `Map` parameter in the contstructor. While the `A.map` field cannot take another _reference_ (because it is final), the _value_ can be modified from the outer world. For example: `Map m = new HashMap(); A a = new A(1, m); m.put(2,"test");` – Julien Kronegg Jun 15 '18 at 19:26
  • Yeah, map shouldn't be used directly, copying the map value to new one and then referencing it to instance variable should be fine. – Nitesh Kumar Thakur Oct 28 '18 at 11:07
-1

Immutable classes should be declared final. If not, you may end up breaking things when mutable subclass instances are used where immutability is expected. Suppose you have the following class:

public class ImmutableTest {
    private final String value;
    private final int count;
    public ImmutableTest(String value, int count) {
        this.value = value;
        this.count = count;
    }
    public String getValue() {
        return this.value;
    }
    public int getCount() {
        return count;
    }
    public boolean equals(Object other) {
        if (this == other) return true;
        if (other instanceof ImmutableTest) {
            ImmutableTest otherObject = (ImmutableTest) other;
            return count == otherObject.count && value.equals(otherObject.value);
        }
        return false;
    }
    public int hashCode() {
        return 31*value.hashCode() + count;
    }
}

Everything's hunky-dory, right? So in client code you set up a hash map using ImmutableTest keys:

Map<ImmutableTest, String> myMap = new HashMap<>();

and populate it. Now suppose you subclass ImmutableTest:

public class SubImmutable extends Immutable {
    private int reps;
    public SubImmutable(String value, int count) {
        super(value, count);
        reps = 1;
    }
    public int getReps() {
        return reps;
    }
    public void repeat() {
        reps++;
    }
    public boolean equals(Object other) {
        if (super.equals(other) && other instanceof SubImmutable) {
            SubImmutable otherObject = (SubImmutable) other;
            return reps == otherObject.reps;
        }
        return false;
    }
    public int hashCode() {
        return 31 * super.hashCode() + reps;
    }
}

Now, if you populate the map using instances of SubImmutable as keys, and if one of those keys is mutated by a call to repeat(), your map breaks. If you try to fix this by declaring equals() and hashCode() final, you can't properly implement SubImmutable to take into account the current state of the rep field.

The only thing that works is to declare ImmutableTest final. Then SubImmutable would be forced to be a container class, rather than a subclass:

public class SubImmutable {
    private final Immutable immutable;
    private int reps;
    public SubImmutable(String value, int count) {
        immutable = new Immutable(value, count);
        reps = 1;
    }
    public String getValue() {
        return immutable.getValue();
    }
    public int getCount() {
        return immutable.getCount();
    }
    public int getReps() {
        return reps;
    }
    public void repeat() {
        reps++;
    }
    public boolean equals(Object other) {
        if (other instanceof SubImmutable) {
            SubImmutable otherObject = (SubImmutable) other;
            return immutable.equals(otherObject.immutable) && reps == otherObject.reps;
        }
        return false;
    }
    public int hashCode() {
        return 31 * immutable.hashCode() + reps;
    }
}
Ted Hopp
  • 308
  • 1
  • 10