Assignment of final fields outside of constructors
Nir Lisker
nlisker at gmail.com
Fri Jul 7 00:17:15 UTC 2023
Hi,
I believe that the following is a known request. Consider the class
class Boat {
final int sails;
Boat(Type type) {
switch (type) {
case ENGINE -> sails = 0;
case SAIL -> sails = 2;
default -> sails = 1;
}
}
}
This is fine (and yes, can be written better). However, if I try to extract
the constructor body into a private method, I get a compilation error that
the final field has not been assigned. Understandable at this point.
In some classes that are more complex than my contrived example, the
constructor's body is divided into steps ('createSails', 'createAnchor'...)
that assign final fields. The current situation forces all of the code to
appear in one big chunk in the constructor, instead of being able to divide
the work into methods, as we usually like to do with methods. There is a
known clear readability and maintenance benefit here.
I'm aware that there is a school of thought saying that the constructor
should only do simple things, and all the preparation should be done before
instantiating the class. Basically, calling createSails() etc. first and
then sending the results to the constructor to just do checks and
assignments. I don't always subscribe to this approach, especially since it
exposes implementation details (what if the return type of createSails()
should be internal to Boat only?).
I remember some discussion for "nested methods": allowing methods (and
constructors) to contain other methods; I assume that that would be one way
to solve it. One can also use simple scopes (`{ ... }`) to divide
the method/constructor. However, I would like you to consider the following
solution.
If the method satisfies these conditions:
1. It is private (and thus final)
2. It is called only from the/a constructor
3. It is called only once
then it may assign final fields. The last condition might not be strictly
necessary because there is already a check if the final field has been
assigned already. These conditions can be applied recursively, checking if
the call originates in the constructor and allowing to further divide
the constructors into sub-steps, but I'm willing to wait with this addition.
I think that this can be checked rather easily by static analysis. I CC'd
Archie who has done similar work in JEP 447 (static analysis to remove
unnecessary restrictions in the constructor) to weigh in. I say "rather
easily" because these conditions don't require looking at the class
hierarchy - they are limited to the inspected class only.
Thoughts?
- Nir
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230707/5c27a392/attachment.htm>
More information about the amber-dev
mailing list