Assignment of final fields outside of constructors
Holo The Sage Wolf
holo3146 at gmail.com
Fri Jul 7 14:37:31 UTC 2023
The static analysis is not too complicated, but there are other problems
with the idea.
Allowing this feature will mean that change inside any place of the class
may lead to an error at the constructor, is this feature important enough
to allow that phenomenon? I don't think it is, especially when there is
already a way to refactor logic into a method by returning the value:
```java
Boat(Type t) {
this.snail = getStail(t);
}
```
For more values you can either use a private record for the carrier object,
or even a local class as a carrier.
About your complaints of "keep constructor stupid", a way to not get into
visibility problems is to make a factory class that is a subclass of Boat,
but this can make the file Boat.java a bit bloated.
Alternatively, make a mirror types inside the factory class, which allows
you to translate the result of the calculations (the mirror types) into the
actual types inside the constructor using a simple lookup.
On Fri, Jul 7, 2023 at 3:18 AM Nir Lisker <nlisker at gmail.com> wrote:
> 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
>
--
Holo The Wise Wolf Of Yoitsu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230707/a9b54d2d/attachment-0001.htm>
More information about the amber-dev
mailing list