Reputation: 59
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
Reputation: 338775
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
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
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
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
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