1

I was reading Effective java Third edition. The topic is

Item 17 : Minimize mutabitity

In this topic Immutable Objects are discussed and also alternative way to not to make class final but still it will be immutable.

Here is what is written in the topic:

Recall that to guarantee immutability, a class must not permit itself to be sub classed. This can be done by making the class final, but there is another, more flexible alternative. private or package-private and add public static factories in place of public constructor.

I can agree on making a constructor private it will make the class unable to be sub classed. But class with package private constructor can be sub classed with in the package. So will the class still be immutable with package private constructor?

Edit 1 :

The class with package private constructor is still immutable for class outside the package. But is this approach helpful?

2
  • 2
    It's not guaranteed to be, no. A mutable subclass can be created in the same package. Commented Feb 23, 2019 at 14:10
  • 1
    the package maintainer may decide that that is a good enough enforcement of immutability if they are primarily concerned about users of the package Commented Feb 23, 2019 at 14:10

2 Answers 2

1
public class BaseClass { private final String arg1; private final String arg2; public BaseClass(final String arg1, final String arg2) { this.arg1 = arg1; this.arg2 = arg2; } public String getArg1() { return arg1; } public String getArg2() { return arg2; } } 

Subclassing this class is possible, but values for arg1 and arg2 must always be provided, and cannot be changed. Obviously by subclassing it you can override the two getter methods.

My approach to avoid possible mistakes by me and co-workers is programming to interface(s).

public interface BaseInterface { String arg1(); String arg2(); } 

Using this interface all-around the codebase will ensure a reduced amount of errors, as the internal state might be changed only at creation point. Once the object escape the creation point, it can no more be changed (well it can, but you have to manually downcast it, and that is easy to spot).


Another strategy to avoid subclassing (and possibly mutability) is using annotation processors. That will enforce your rule at compile time.

Sign up to request clarification or add additional context in comments.

8 Comments

+1 for not parentizing on the other hand: immutable for me is somehow connected with putting something in a container and signaling users that retrieve that something: "stop, don't change this thing unless you know exactly what you are doing." I think you missing that case and only explain subclassing.
"You can maintain a class immutable and allow subclassing at the same time." Not if you want to keep the features of immutability that most users would find most useful. If I read that a class is immutable, I generally expect to be able to assume that no aspect of its state will change. I can cache it for as long as I like, and it will be fine. If a mutable subclass is created, state can be changed under my code's feet - even if it's an aspect of the class that I never use, if I pass references to the object around expecting it to be immutable, that can cause issues.
@JonSkeet you're right. I specified the behavior (but not the data) can be changed when subclassed. That's why I also wrote "program to interfaces", so to reduce the amount of errors over time. If the code is not yours (maybe from another library) the annotation processor idea isn't bad, to enforce a specific rule.
If you're not even making the getArg1() methods final, then it's not even immutable with respect to those methods. That's not what I'd view as immutable, and it definitely doesn't retain many of the benefits of immutability. Programming to interfaces is all well and good, but that's another case where you really can't guarantee immutability - whereas often that's a very useful thing to be able to do. The question is about immutability - you're suggesting options which don't guarantee immutability.
@JonSkeet sure! Well than I'll have to remove the word "immutable" from the answer, as maybe I misused it.
|
0

Having package-private constructor does not guarantee 100% immutability, as was already mentioned in the comment:

A mutable subclass can be created in the same package

Developing a jar library you may conclude some agreements within the team and the package maintainers, what of couse doesn't not enforce this programmatically, but still is somehow under the control of the product developers.

And what about others? One can create classes outside of you library but in the same package and in multiple locations in the classpath.

If you have such cases and concerned with questions like that, Package Sealing comes into play.

Packages within JAR files can be optionally sealed, which means that all classes defined in that package must be archived in the same JAR file. You might want to seal a package, for example, to ensure version consistency among the classes in your software.

If you want to guarantee that all classes in a package come from the same code source, use JAR sealing. A sealed JAR specifies that all packages defined by that JAR are sealed unless overridden on a per-package basis.

Let's say that your jar libarary has a class com.example.Factory with protected/package-private members and the package com.example is sealed. An attempt creating a class com.example.Accessor that tries to access those members should fail.

That's how you can ensure that package-private members are accessed by the limited (and potentially trusted) group of members.

Returning to the main question:

So will the class still be immutable with package private constructor?

With strong agreements between maintainers, careful code review and package sealing it can become kind of immutable.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.