If this is really something that's desired, then why use an annotation when there's a keyword already available: const. However, there's a reason that's never been actually used in Java, and that's because it's so hard to get right.

On 26/11/2021 00:11, Alan Snyder wrote:
I like the idea of an @Immutable annotation. So much so, that I have created 
one and use it heavily.
However, my @Immutable is just for documentation; it does not have a rigorous 
semantics that would be
needed for a compiler to validate its use or generate different code.
But it could be used by lint-like tools to warn against suspicious code.

My @Immutable applies only to classes and interfaces, not variables or type 
parameters.
A class or interface is either @Immutable or it is not.
That avoids much complexity.

I think it is fine to have an @Immutable list (type) whose elements are mutable.
Is an instance of an immutable list with mutable elements immutable or not?
Tell me why you want to know and I might be able to answer the question.

I also think there are cases where an @Immutable type might have a mutable 
implementation
(in other words, it might have instance variables that are not final or have 
non-immutable types).
Examples include internal caching and configurability of performance or 
incidental behavior such as logging.

In general, immutability is in the mind of the beholder.

A case can be made that an interface or class that is @Immutable should only 
permit
extensions and implementing classes that are @Immutable.
Perhaps a compiler could validate that, but only at compilation time
(to ensure binary compatibility when an @Immutable annotation is added to an 
existing class or interface).

The idea of the compiler trying to enforce a semantics of immutability is scary.
Java tried to do this with uninitialized and final instance variables, and the 
result has been a disaster.
The rules make semantically valid code illegal, forcing complex workarounds.
(For example, suppose you want to compute a value, bind it to a final instance 
variable, and pass it to the superclass constructor.)
Yet it is still possible, actually quite easy, to write code that accesses 
uninitialized final variables.
I have code that checks a final @NotNull instance variable to ensure that it is 
not null.
IDEA tells me the check is unnecessary, but IDEA is wrong.

Immutability is a much more complicated concept than uninitialized.
Not something the compiler should mess with.

   Alan



On Nov 25, 2021, at 10:10 AM, Ralph Goers <ralph.go...@dslextreme.com> wrote:

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.hoversh...@paf.com> 
wrote:

On Thu, 25 Nov 2021 at 10:03, Andrew Haley <aph-o...@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>

Reply via email to