Adding an @Immutable annotation to Java

Ralph Goers ralph.goers at dslextreme.com
Thu Nov 25 18:10:00 UTC 2021


I would think that if a class is marked immutable that would imply all the fields in it and from its inherited 
classes should be immutable. If fields are marked immutable then it would only apply to them.

What I wonder is what its relationship to final would be. The final annotation implies that a field must be 
set in the constructor and cannot be modified after that. I would imagine that @immutable to have to imply 
@final but would also apply at runtime. For example, where declaring a List field as final means you cannot 
replace the List once it is set. I would expect @Immutable to do the same but also mean that you cannot 
add elements to the List through that reference. But that would also mean that you cannot pass the reference 
to another variable that isn’t also annotated with @Immutable - unless the immutable attribute becomes some 
kind of internal flag on the object.

To be clear, this concept is always something I have wanted in Java. It is a real pain to have to do things like
List<String> list = Collections.unmodifiableList(List.of(“foo”, “bar”));

Instead, it would be nice to be able to do
@Immutable List<?> list = List.of(“foo”, “bar”);

Although the two could be implemented to do the same thing, the second could prevent passing the field in a 
parameter that wasn’t declared @Immutable. Likewise, passing a non-immutable list in a parameter annotated 
with @Immutable could cause the list to be copied to an immutable list automatically.

Ralph

> On Nov 25, 2021, at 2:49 AM, Mariell Hoversholm <mariell.hoversholm at paf.com> wrote:
> 
> On Thu, 25 Nov 2021 at 10:03, Andrew Haley <aph-open at littlepinkcloud.com>
> wrote:
> 
>>> Quick question, out of curiosity: how would it behave with respect to
>>> inheritance? Can a @Immutable class inherit from an non immutable one?
>> 
>> And: does @Immutable mean deeply immutable? IMO it really should,
>> but that's harder to check, and we'd have to think about what this
>> means for binary compatibility.
>> 
> 
> As cited in the original email,
>> and the programmer could, for example, annotate a new record object with
> @Immutable only if all its fields are annotated with @Immutable.
> 
> I would infer from this that it would mean deeply immutable.
> To clarify further, the following record `Wrapper` would be legal only
> because `A` has `@Immutable` on its _type_:
> 
>    @Immutable class A {}
>    @Immutable record Wrapper(A a) {}
> 
> while this would not be legal:
> 
>    class A {}
>    @Immutable record Wrapper(A a) {}
> 
> because `A` is not `@Immutable`. This could however borrow from the Rust
> concept of "auto-traits" (read: automatically apply certain traits or
> capabilities depending on how the type is defined), and infer `@Immutable`
> if it is deeply immutable, but that would raise the question of API &
> binary compatibility again.
> 
> It would also not be legal with this:
> 
>    @Immutable
>    class A {
>        private int value;
>        public void value(int value) { this.value = value; }
>    }
> 
> because `value` is _not_ immutable.
> 
> Moving on from your question, this then poses the question of memoizing
> potentially very expensive optimisations, such as the following, currently
> found in the JDK:
> https://github.com/openjdk/jdk/blob/f0136ec94539d0e30ec11d44f8143196da1f7125/src/java.base/share/classes/java/lang/String.java#L2323-L2343
> Code above from `java.base/java.lang.String`:
> 
>    public int hashCode() {
>        // [snip large comment]
>        int h = hash; // [hash is a property on the type]
>        if (h == 0 && !hashIsZero) { // [hashIsZero is a property on the
> type]
>            h = isLatin1() ? StringLatin1.hashCode(value)
>                           : StringUTF16.hashCode(value);
>            if (h == 0) {
>                hashIsZero = true;
>            } else {
>                hash = h;
>            }
>        }
>        return h;
>    }
> 
> If the compiler / JRE were to infer the immutability from setters, it would
> again pose a new question of how to detect such. It could be possible that
> adding a new keyword or annotation to ignore mutable fields would be
> necessary (say `private mutable int hashCode;` as an example of my
> thoughts). At that point, however, what is the difference to not having
> this feature at all?
> 
> This is definitely an idea I like as a user of the language, but it's
> something that would require a bit of work, assuming it can be boiled down
> to something feasible at all.
> 
> -- 
> 
> *Mariell Hoversholm *(she/her)
> 
> Software Developer @ <https://aboutpaf.com>
> 




More information about the core-libs-dev mailing list