him_t
him_t

Reputation: 59

Is this class immutable or not?

The below class doesn't have final keyword but its member variables are private and final and the class exposes no mutate/set methods. Is this class immutable or not?

public class Abc {

    private final int id;
    private final String name;

    public Abc(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }
}

Upvotes: 3

Views: 429

Answers (5)

Basil Bourque
Basil Bourque

Reputation: 338775

Records

Other Answers directly addressed your questions about immutability, class being marked final, and subclasses being mutable. I’ll add an alternative option to more briefly accomplish your goal of immutability: Records.

Java 16 brings the new records feature. If the main purpose of your class is to immutably and transparently carry data, define your class as a record. The compiler implicitly creates default constructor, getters, equals & hashCode, and toString.

A record is implicitly final, so no risk of a subclass becoming mutable.

Declare the properties in parentheses. By default, you need not put anything in the curly braces body of a record.

record Abc ( int id , String name ) {}

Instantiate like any other class.

Abc x = new Abc ( 42 , "Snuffleupagus" ) ;

The implicit getter methods are simply the property names. The JavaBeans-style get…/is… method naming is not used. (You could add such methods if required.)

System.out.println( x.name() ) ;

Snuffleupagus

Upvotes: 2

Burak URAY
Burak URAY

Reputation: 119

It is mutable if its internal states can be changed after the class is created.

In your example, although there is no class final, the inside situations cannot be changed again because of final keyword. In this way, the class becomes immutable again

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500805

The class itself is immutable, yes - if you create an instance of just Abc, no aspect of that can be changed after the instance has been created.

However, that doesn't mean that any code receiving a parameter of type Abc can assume it's immutable with all the benefits that carries... because the class isn't final. It's entirely possible for an object of a type compatible with Abc to be mutable:

public class Mutable extends Abc {
    private String value;

    public Mutable(int id, String name) {
        super(id, name);
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override public String toString() {
        return value;
    }
}

Now imagine you've got code which deals with an Abc:

public class AbcConsumer {

    private final Abc abc;

    public AbcConsumer(Abc abc) {
        this.abc = abc;
    }

    // No need to create a defensive copy or anything like that...
    // abc is immutable, right?
    public Abc getAbc() {
        return abc;
    }
}

Here the consumer assumes it's fine to treat Abc as if it's an immutable class - but if someone creates an AbcConsumer by passing in a Mutable instance instead of "vanilla" Abc instance, it could cause problems.

That's why it's generally a good idea when you're creating an immutable type to make it final as well - that way any consumers know that if they receive a reference with that type, it's definitely immutable.

To put it another way: yes, the Abc class is immutable... but you can't assume that a reference with a compile-time type of Abc refers to an immutable object.

Upvotes: 12

bliss
bliss

Reputation: 336

As presented, yes, the class is immutable.

The "final" keyword on a class declaration prevents it from being extended - it's not related to immutability (unless your variables are declared public or protected).

Edit; "not related" is a poor choice of words, please see Jon Skeet's answer below

Upvotes: 7

rzwitserloot
rzwitserloot

Reputation: 103018

No, it is most likely not.

A problem is terminology. What do you mean by class? If you mean this code, sure, it's immutable. But 'this code' is not something that is particularly relevant to the concept of immutability. That usually makes a lot more sense if we consider it: this type.

As in, is the type Abc immutable?

As in, given:

public void foo(Abc abc) { ... }

is it safe to assume that the received abc couldn't possibly change?

And then the answer is no. That is not safe to assume: The type Abc is mutable.

The reason is that someone could make this:

class SneakyAbc extends Abc {
    private int id;

    public void setId(int id) {
       this.id = id;
    }

    public String getId() {
       return id;
    }
}

This is why immutable classes are virtually always made final, to fully guarantee it.

Depending on how fancy you want to paint with the brush of 'what does this term mean', if all methods of Abc are final, you can consider it immutable as well if you really want to: Whilst the class does not need to be immutable (a subclass can add a new non-final field and create getters and setters for this), all the stuff you can 'witness' from the Abc type, assuming you don't use reflection, do appear immutable.

Exactly what definition of immutable you go with would be required knowledge to delve any further.

Note that something like java.io.File has only final fields and is final, and yet, it has easily observable state that can be modified: just.. delete the file, and voila you can see it. You can pull similar stunts with an IdentityHashMap to create a faux but nevertheless very observable 'field'.

Thus, 'immutable' as a concept: Useful. As a boolean flag to bestow upon a certain type or some java source file: Not useful.

Upvotes: 6

Related Questions