[HN Gopher] JEP draft: Prepare to make final mean final
       ___________________________________________________________________
        
       JEP draft: Prepare to make final mean final
        
       Author : mfiguiere
       Score  : 199 points
       Date   : 2025-03-31 19:35 UTC (1 days ago)
        
 (HTM) web link (openjdk.org)
 (TXT) w3m dump (openjdk.org)
        
       | Almondsetat wrote:
       | final ly
        
       | stickfigure wrote:
       | Great! Now can we make `final` the default for all fields,
       | variables, and parameters?
       | 
       | (yes yes, I know, that would break syntax... but please come up
       | with something to discourage mutability)
        
         | magicalhippo wrote:
         | Const-ness in C++ is something I miss in other languages. Being
         | immediate able to see that this function or method couldn't
         | mutate the object made it so much easier to reason about the
         | code.
         | 
         | Yeah I know there's ways around it, but then the author known
         | what they told the other party to expect.
        
           | josephg wrote:
           | Yeah I find it a bit startling going from rust (where const
           | is the default) to basically any other language. Sometimes I
           | look at typescript function definitions and I'm like -
           | uuuuhhh does this function mutate that parameter? Does it
           | keep a reference to it? If the object I'm passing is mutated
           | after I call this function, will something break? It's
           | impossible to tell from a function signature, even with all
           | of typescript's type safety. That gives me the willies. - and
           | for good reason, it's tripped me up lots of times.
           | 
           | Even the JS standard library struggles with this. You just
           | have to remember that .sort() modifies the array in place
           | (and returns it), but .slice() does a shallow clone of the
           | array. (Not a deep clone - that would be different again!)
        
             | mont_tag wrote:
             | > Even the JS standard library struggles with this. You
             | just have to remember that .sort() modifies the array in
             | place
             | 
             | ISTM that there are almost always some kinds of mutable
             | data structures present in non-trivial programs. And
             | outside of the program, you have databases to directories
             | of files that get mutated by users or other parts of a
             | program. I think this is just a fact of life.
        
               | magicalhippo wrote:
               | The point isn't that there's mutability, it's to be able
               | to easily identify where it is and where it is not.
               | 
               | Languages which lack the tools to do this are just harder
               | to reason about, at least for me.
               | 
               | In the JS example, as a non-JS coder, I would expect that
               | a sort function that returns nothing/void would sort in-
               | place, while a sort function that returns an array would
               | return a sorted copy. I would _not_ expect it to sort in-
               | place and return it.
               | 
               | However in C++ I could easily see from the function
               | definition that it might be doing that, because a sort
               | that returns a copy would take a const reference for the
               | input array. So if I came across a sort function which
               | took a non-const reference as input I'd be able to at
               | least suspect it's doing it in-place.
        
             | recursive wrote:
             | > Even the JS standard library struggles with this. ...
             | 
             | I don't think this represents struggling. There needs to be
             | _some_ way to sort in place. Sometimes you need to sort a
             | big array and don 't want to allocate.
             | 
             | And there should be _some_ way to clone the array without
             | mutating. That 's slice. So you how do you sort a clone?
             | .slice().sort()
             | 
             | I think by far the biggest problem with ES .sort() is that
             | number arrays don't sort numerically by default.
        
               | steveklabnik wrote:
               | > There needs to be some way to sort in place. Sometimes
               | you need to sort a big array and don't want to allocate
               | 
               | Your parent isn't saying there shouldn't be mutation,
               | just that the mutation should be obvious.
               | 
               | In Rust, the type signature for the in-place sort is
               | pub fn sort(&mut self)       where           T: Ord,
               | 
               | That `&mut self` lets you know that it's going to mutate.
        
               | josephg wrote:
               | Exactly. And for function parameters, rust also (usually)
               | makes it obvious when you're passing by value, by
               | reference or by mutable reference:
               | foo(x); // Moves or copies         foo(&x); // immutable
               | reference         foo(&mut x); // mutable reference
               | 
               | I don't need to look up the signature of foo to
               | understand what happens to my variable. It's obvious at a
               | glance.
        
               | recursive wrote:
               | Fair point. I guess this is the cost of dynamic
               | languages.
        
           | titzer wrote:
           | If possible, 1) design completely immutable data structures
           | that can be broadly shared and don't need to be copied. If
           | you need mutability, just embrace the fact that someone is
           | going to abuse mutability and 2) try to create abstractions
           | that can suffer abuse. If you're coming from a language that
           | doesn't have const, you learn to build things that are
           | hard(er) to screw up.
           | 
           | While bad code can exist in any language, I get worried about
           | too much const in code, because it means they failed at both
           | 1) and 2) and instead there are usually seriously tricky
           | protocols that must be observed to make the thing work. I
           | often ran into code where people were sprinkling const all
           | over the code to lock things down but they fundamentally did
           | not understand the design and made it nearly impossible to
           | evolve, unless you used casts to get rid of const, which
           | defeats the whole purpose.
           | 
           | I'm not saying const doesn't have value, but it's weapon #3,
           | not weapon #1.
        
             | magicalhippo wrote:
             | > sprinkling const all over the code to lock things down
             | 
             | That's like using a hammer on a screw, clearly not the
             | right way.
             | 
             | Thankfully I've never worked on such codebases.
        
         | duskwuff wrote:
         | What man that sees the ever-whirling wheel       Of Change, the
         | which all mortal things doth sway,       But that thereby doth
         | find, and plainly feel,       How Mutability in them doth play
         | Her cruel sports to many men's decay?
         | 
         | (Edmund Spenser, 1596)
        
         | xxs wrote:
         | >but please come up with something to discourage mutability)
         | 
         | Records?
        
         | throwaway92422 wrote:
         | Records in Java have final fields.
        
         | nirvdrum wrote:
         | I'd be happy with a `val` keyword. I thought that was a
         | seriously missed opportunity when they introduced `var`.
        
       | jjmarr wrote:
       | From my perspective as a C++ developer, every attempt to use
       | `const` for compiler optimization appears to be stymied by the
       | existence of `const_cast`, because modifying a `const` value is
       | only undefined behaviour if the underlying object is `const`.
       | Glad to see that Java is willing to break the language to improve
       | it.
        
         | mb7733 wrote:
         | > modifying a `const` value is only undefined behaviour if the
         | underlying object is `const`.
         | 
         | I found this sentence confusing. You mean that modifying a
         | value that has been const_cast is undefined behaviour only if
         | the original variable was const right? Or something else?
        
           | oconnor663 wrote:
           | (Rereading your comment, it sounds like you might already
           | know all of this. Apologies.)
           | 
           | If I understand correctly, here's a C++ example that has
           | undefined behavior:                   int foo() {
           | const int x = 42;             int *p = (int *)&x;
           | *p += 1;             return x;         }
           | 
           | foo() is UB, because it modifies the const object x, using a
           | pointer cast to "cast away const". (Unfortunately, UBSan
           | doesn't catch this, and I'm not aware of any sanitizer that
           | does.) It's tempting to say that the pointer cast is "at
           | fault" for the UB, but consider this very similar example
           | that's _not_ UB:                   int bar() {
           | int x = 42;             const int *const_p = &x;
           | int *p = (int *)&const_p;             *p += 1;
           | return x;         }
           | 
           | bar() has exactly the same pointer cast as foo(), however in
           | this case the original x object is _not_ const. That makes
           | "casting away const" legal in this case. So the problem we're
           | left with, is that knowing all the types isn't enough for us
           | to tell whether this cast is going to cause UB. We have to
           | know where the pointer _originally_ came from, which might be
           | in another function or another file.
        
             | mb7733 wrote:
             | Right, gotcha, I just found it confusing to say "modifying
             | a `const` value" but if course you meant modifying the
             | underlying value [through a cast]. All good.
        
         | PhilipRoman wrote:
         | The implementation of "const" in C/C++ is really annoying, in
         | the end all you get is a bit of semantic documentation and a
         | bunch of compiler errors or casts whenever some library doesn't
         | use it. You can often trick the compiler into stronger
         | optimizations by making a local variable marked as "const" (in
         | which case it is a "true const") and copying the value to/from
         | it.
        
       | magicalhippo wrote:
       | So this is about warning about using deep reflection or such to
       | modify fields marked as final.
       | 
       | Not a Java dev, so I thought it might be related to classes
       | marked final somehow. But this seems like a reasonable proposal,
       | at least in spirit.
        
       | bironran wrote:
       | A cursory glance at "setAccessible" usage reveals popular
       | libraries such as serializers like gson and jaxb, class
       | manipulation and generation like cglib, aspectj and even
       | jdk.internal.reflect, testing frameworks and libraries including
       | junit, mockito and other mocking libraries, lombok, groovy,
       | spring, and the list goes on and on.
       | 
       | My bet is that this will be yet another "checked exception" or
       | "module system", where many applications now need to add "--add-
       | opens". If you'll use ANY of many of the more popular frameworks
       | or libraries you'll end up giving this assurance away, which will
       | make library developers not able to rely on it and we're back to
       | square one.
        
         | PathOfEclipse wrote:
         | setAccessible is also used to be able to access private fields,
         | and not just to be able to write to final fields. Most
         | libraries shouldn't need to set final fields, and I say this as
         | someone who was very against when they deprecated
         | java.lang.misc.Unsafe. I've only had to set a final field once
         | in my career and it was related to some obscure MySql/JDBC
         | driver bug/workaround. This particular deprecation seems very
         | sensible to me.
        
           | eastbound wrote:
           | So how should GSON initialize an object?
           | 
           | The theory is, go through the constructor. However, some
           | objects are designed to go through several steps before
           | reaching the desired state.
           | 
           | If GSON must deserialize {..., state:"CONFIRMED"}, it needs
           | to call new Transaction(account1, account2, amount), then
           | .setState(STARTED) then .setState(PENDING) then
           | .setState(PAID) then .setState(CONFIRMED) ? That's the theory
           | of the constructor and mutation methods guarding the state,
           | so that it is physically impossible to reach a wrong state.
           | 
           | There is a convention that deserialization is an exception to
           | this theory: It should be able to restore the object as-is,
           | after for example a transfer over the wire. So it was
           | conventionally enabled to set final variables of the object,
           | but only at initialization and only for its own good. It was
           | assumed that, even though GSON could reach a state that was
           | unachievable through normal means, it was, after all, the
           | role of the programmer to add the right annotations to avoid
           | this.
           | 
           | So how do we do it now?
        
             | vips7L wrote:
             | Why would you use GSON for objects that go through steps of
             | state? Why would you mark fields like State as final when
             | it is actually mutable? This just sounds like poorly
             | designed code.
             | 
             | Maybe I don't know of your use case, but GSON/Jackson/Json
             | type classes are strictly data that should only represent
             | the data coming over the wire. If you need to further
             | manipulate that data it sounds like the classes have too
             | much responsibility.
        
               | bdangubic wrote:
               | all state is immutable :) a change creates new state -
               | which is immutable
        
               | vips7L wrote:
               | :) no its not.
        
               | bdangubic wrote:
               | if you change the state, it is not same state, it is a
               | new state
        
             | steveklabnik wrote:
             | > So how do we do it now?
             | 
             | The JEP says:
             | 
             | > the developers of serialization libraries should
             | serialize and deserialize objects using the
             | sun.reflect.ReflectionFactory class, which is supported for
             | this purpose. Its deserialization methods can mutate final
             | fields even if called from code in modules that are not
             | enabled for final field mutation.
             | 
             | I don't know enough about the details here to say if that's
             | sufficient, but I imagine that it at least should be, or if
             | it's not, it will be improved to the point where it can be.
        
               | cesarb wrote:
               | > The JEP says: [...]
               | 
               | The JEP also says:
               | 
               | > The sun.reflect.ReflectionFactory class only supports
               | deserialization of objects whose classes implement
               | java.io.Serializable.
               | 
               | In my experience, most classes being deserialized by
               | libraries like GSON do _not_ implement Serializable.
               | Implementing Serializable is mostly done by classes which
               | want to be serialized and deserialized through Java 's
               | native serialization format (which is used by nothing
               | outside Java, unlike cross-platform formats like JSON or
               | CBOR).
        
             | twic wrote:
             | It strikes me that we could have a way to reflectively
             | create an object from values for all its fields in a single
             | step - similar to what record constructor does, but for any
             | class (could even be Class::getCanonicalConstructor,
             | returning a java.lang.reflect.Constructor). It would be
             | equivalent to creating an uninitialised instance and then
             | setting its fields one by one, but the partly-initialised
             | object would never be visible. This should probably be
             | restricted, because it bypasses any invariants the
             | constructor enforces, but as you say, ultimately
             | serialisation libraries do need to do that.
        
               | recursivecaveat wrote:
               | I don't know if Java serialization supports this kind of
               | thing, but if you have object A has a pointer to object B
               | and vice-versa, there's no order to deserialize them
               | without passing through a partially-initialized state
               | that preserves the object identity relationship. I
               | suppose you can't construct this kind of loopy references
               | graph with final fields without reflection in the first
               | place, so it's kindof chicken and egg. For the very
               | common case of DAG-shaped data or formats that don't
               | support references I think the one-shot internal
               | constructor works though.
        
             | n_plus_1_acc wrote:
             | Just do it like Rust
        
         | merb wrote:
         | Yeah like the module system. Looks good on paper, is probably
         | hard to deal with. There are still tons of popular libraries
         | that have no module-info. Java does evolve, but the direction
         | it does is so weird. And than the tooling is strange and it's
         | worse that there are basically two build tools, both with their
         | upsides and downsides but they still feel more complicated than
         | tools for other languages like cargo, go (if you consider
         | that), msbuild (the modern csproj stuff/slnx)
        
         | pron wrote:
         | We've addressed that in the JEP. Serialization libraries have a
         | special way to circumvent this (until we get Serialization
         | 2.0), and mocking libraries may, indeed, need to set the flag,
         | but they're rarely used in production, so if they don't enjoy
         | some new optimisation -- no big deal.
         | 
         | BTW, this JEP does not apply to setAccessible generally, as
         | that's been restricted since JDK 16, but only to the particular
         | (and more rare) use of setAccessible to mutate instance final
         | fields. As the JEP says, static final fields, records' internal
         | instance fields, and final instance fields of hidden classes
         | cannot be mutated with that approach currently, so it's never
         | been something that's expected to work in all cases.
        
           | LadyCailin wrote:
           | Would be nice to have a single "--test-mode" flag that is
           | only meant to be set when running tests, and allows for all
           | this leniency, (add opens, etc) in a single flag.
        
             | pron wrote:
             | We should separate the problem from the solution. The
             | problem is that running tests may require relatively many
             | integrity-busting flags. That is true.
             | 
             | There are, however, better solutions than a global test-
             | mode flag that, invariably, will be used by some in
             | production out of laziness, leaving no auditable record of
             | what integrity constraints need to be violated and why.
             | When a new team lead is appointed some years later they
             | will have a hard time trying to reduce the entropy.
             | 
             | The better solutions will arrive in due course, but until
             | then, build tools can automatically add most of the
             | necessary flags. They should be encouraged to do that.
        
               | LadyCailin wrote:
               | So make the flag remove some other feature, which is
               | critical to production, like the ability to run main() or
               | something.
               | 
               | On the other hand, I don't think the solution to someone
               | holding a shotgun to their foot and threatening to pull
               | the trigger is to make everyone wear armored shoes.
               | They're already a lost cause, and there are a billion
               | other ways they can shoot their foot off, if they are so
               | inclined.
               | 
               | I agree with the principal of making it hard to screw
               | things up assuming good faith efforts (making it hard to
               | fall in the pit of despair), so overall I like the JEP.
        
               | pron wrote:
               | > On the other hand, I don't think the solution to
               | someone holding a shotgun to their foot and threatening
               | to pull the trigger is to make everyone wear armored
               | shoes.
               | 
               | I don't think so, either, it's just that I think there
               | are better solutions than a test-mode flag _at the level
               | of the `java` launcher_. If the mechanism that runs the
               | tests can automatically configure the appropriate
               | capabilities without requiring the user running the tests
               | to do manual configuration then the problem is solved for
               | those who just want to easily run tests just as well as a
               | test-mode configuration.
               | 
               | The idea of a test-mode flag has been floated before and
               | considered; we're not ruling it out, but if such a mode
               | is ever added, I can't tell you now what it would mean
               | exactly. In any event, it's better to carefully study the
               | nature of the problem and its origins before suggesting a
               | particular solution. As Brian Goetz likes to say, today's
               | solutions may well become tomorrow's problems.
               | 
               | > They're already a lost cause, and there are a billion
               | other ways they can shoot their foot off, if they are so
               | inclined.
               | 
               | True, but our experience shows that it's not a good idea
               | to make the bad choice the easiest one, or people may
               | pick it out of laziness. Let those who want to shoot
               | themselves in the foot work for it. If nothing else, it
               | increases the chance that they learn what their (not-
               | entirely-trivial) configuration means, and maybe they'll
               | realise they don't want it after all.
               | 
               | Someone might point out that there are still ways to do
               | the wrong thing out of laziness by blindingly copying a
               | configuration from StackOverflow etc., but we're not done
               | yet.
        
         | PaulHoule wrote:
         | My impression is that this will be painful for the code I work
         | on because the libraries you mention depend on being able to
         | modify private and/or final fields.
        
           | xxs wrote:
           | private fields are no issue
        
       | keybored wrote:
       | I don't see a way to opt-in to a hard error if this happens
       | somewhere in the guts of the code (third-party probably).
        
         | steveklabnik wrote:
         | > --illegal-final-final-mutation=deny will result in Field::set
         | throwing an IllegalAccessException for every illegal final
         | field mutation.
         | 
         | Seems like this way?
        
       | aardvark179 wrote:
       | So happy to see this, thank you Ron and Alan.
        
       | Traubenfuchs wrote:
       | I would like to see some realistic example scenarios where this
       | could actually lead to a speedup and how much it actually speeds
       | up.
        
         | kelnos wrote:
         | The article mentions one: const folding. I don't think we need
         | a benchmark to suggest that could mean a performance
         | improvement in some cases.
         | 
         | Regardless, to me this isn't about performance, this is about
         | "integrity" (to use the same term as in the JEP): consumers of
         | my library should not be mucking about in private
         | implementation details, and then inevitably complaining about
         | problems to me when something breaks. If you need a feature
         | that I don't expose, ask for it (or better yet, submit a
         | patch).
         | 
         | Sure, I've used reflection to modify library internals before,
         | but I recognize that whenever I do that I'm inviting a
         | maintenance headache into my world. But some people just think
         | things should always work, even when they are breaking them.
        
       | elric wrote:
       | I've written my fair share of nasty reflexive code for testing or
       | for abusing libraries, but I don't think I've ever overwritten
       | final fields in this way. Private fields, sure. But not final.
       | 
       | Sounds like a good evolution to me.
        
       | cogman10 wrote:
       | Hmm, not a bad approach.
       | 
       | I think the one thing that'd be nice is if I could somehow tell
       | the JVM from a class that this class is open for final mutation
       | rather than needing special flags passed into the JVM or special
       | manifests in the Jar. It's often pretty clear to me, as a dev,
       | what I when I need something to have final mutation (generally
       | only with serialization objects).
       | 
       | For example,                   @FinalMutatableByReflection
       | class Foo {           final String bar;         }
       | 
       | That'd allow me to transition code by just adding an annotation
       | where it needs to be while also getting the benefit that final is
       | really final everywhere else in code that isn't working with
       | serialization.
        
         | nightpool wrote:
         | They actually have a very similar proposal in the draft
         | already:                   The sun.reflect.ReflectionFactory
         | class only supports deserialization of objects whose classes
         | implement java.io.Serializable
         | 
         | That is, you'll still be able to mutate final fields using the
         | ReflectionFactory class, as long as that class inherits from
         | Serializable
        
           | no_wizard wrote:
           | I know this isn't the most relevant but to see the sun
           | namespace still exists gives me a small amount of joy
        
             | xxs wrote:
             | Back in the '90s I used to consider sun.xxx the cooler
             | version of Java.
        
         | owlstuffing wrote:
         | The issue is that many essential libraries and tools rely on
         | setting internal final fields. I assume that's why the options
         | around this have remained open-ended.
         | 
         | The problem with these various "integrity by default" options
         | is that, in most cases, granting access to one effectively
         | grants access to all. For instance, JNI, agent libraries, and
         | JPMS options can each be used to bypass restrictions, making
         | the separation between them largely illusory. Integrity, as
         | framed here, is ultimately binary.
         | 
         | The unfortunate reality of the "integrity by default" crusade
         | is that applications relying on libraries and tools that modify
         | internals will continue to do so. The JDK hasn't filled any
         | gaps--it has only made an already delicate situation worse.
        
           | twic wrote:
           | Not in my experience. I run my applications with default
           | integrity, and when i hit a problem, i find out what caused
           | it, and fix it. Often it's just a matter of upgrading a
           | library to a newer version which doesn't do naughty
           | reflective things, or changing some config to stop it doing
           | so, or changing our own code. We had a serialisation library
           | which did deep reflection to be slightly more efficient at
           | serialising BigDecimal; there was a system property to turn
           | that off, so i set it. We had code doing deep reflection into
           | JMX to get the current PID; i changed it to use ProcessHandle
           | instead. My colleague wrote a little test data library which
           | does some horrific things; i might delete it and adopt
           | Instacio instead.
           | 
           | I think there's only one case where i ended up relaxing
           | integrity, and i'm hoping that's temporary - it will take
           | more time to fix than i was willing to spend.
        
           | pron wrote:
           | First, it's not a "crusade" but the steps necessary to
           | deliver the features Java's users demand. Second, the
           | prevalence of the use of JDK internals has dropped
           | drastically, and demonstrably so. For example, many programs
           | broke before internals were encapsulated during the upgrade
           | from 8 to 9; 99% of the causes were libraries relying on
           | internals, which had changed. Access to internals was closed
           | off in JDK 16, although, as you say, it can be selectively
           | allowed. And yet, between JDK 17 and JDK 23, changes of
           | similar magnitude to the JDK internals caused nearly no
           | upgrade problems. Upgrading the JDK now is smoother and
           | easier than it's been in the last two decades. Why? Because
           | there's been a large reduction in libraries' access to
           | internals.
           | 
           | I think Java's handling of this transition compares very
           | favourably to how other languages have handled similar
           | transitions from some old model to a new one (or evolution in
           | general) in terms of balancing the needs of both old and new
           | projects.
        
             | cogman10 wrote:
             | I can attest to how easy it's become to update jdks for our
             | org.
             | 
             | 8->11 was really a pain in the neck. 11->17 had some pain,
             | but mostly was nothing serious.
             | 
             | 17->21 has been painless.
             | 
             | And I have some projects running on 24 already with no
             | problems.
             | 
             | The feature delivery has been great and we are getting
             | pretty close to not needing to do anything but update the
             | jdk to move forward.
             | 
             | Now, if only I could get devs to stop using lombok....
        
               | sgammon wrote:
               | > Now, if only I could get devs to stop using lombok
               | 
               | One of the only remaining reasons I discard libraries
               | without a further thought
        
               | pandemic_region wrote:
               | Why the Lombok hate? It's just an innocent preprocessor
               | allowing a bit of syntactic sugar right?
        
               | pests wrote:
               | It feels weird to me that functions exist without being
               | in the source code.
        
               | pandemic_region wrote:
               | I never looked at it that way, but I get now where you're
               | coming from. To me, the Lombok annotations are more like
               | @Transactional, providing some useful boilerplate that I
               | don't need to care about, it just works.
        
               | pests wrote:
               | I agree its extremely useful. I am just used to
               | languages/frameworks that at least autogenerate the code.
               | Maybe some inheritance to give you a place to make
               | changes that wont be wiped.
        
               | dingi wrote:
               | It's not an innocent preprocessor. It's a hack which
               | breaks on almost every JDK upgrade.
        
               | mschuster91 wrote:
               | Now, given that a looot of projects use Lombok, it might
               | be worth a try to actually _standardize_ (or at least
               | upstream) the feature given there seems to be an obvious
               | demand of people to not having to pollute their codebase
               | with tons of pointless getter and setter boilerplate.
        
               | cogman10 wrote:
               | AutoValue, Immutables, MapStruct. All libraries that do
               | what Lombok does without using JVM internals.
               | 
               | Records also literally do a huge portion of what Lombok
               | does.
               | 
               | There are a lot of fairly popular alternatives to Lombok
               | out there that don't need a constant eye on maintenance.
               | Lombok is probably the most popular but that's really
               | mostly because it's got the first mover advantage.
        
               | JanecekPetr wrote:
               | - It doesn't work with ErrorProne
               | (https://github.com/google/error-
               | prone/issues?q=sort%3Aupdate...) - you need a special
               | plugin in your IDE for the generated classes to be
               | visible and/or the code to be parsable - it does not work
               | with many other tools, usually there is a bridge to fix
               | that, e.g. with MapStruct:
               | https://mapstruct.org/faq/#Can-I-use-MapStruct-together-
               | with...
               | 
               | Other tools and libraries generally do not interact in
               | such an errorprone manner.
               | 
               | That said, when you know how it works, what it needs, and
               | you know how to iron all of those tiny wrinkles, it works
               | fine and saves you some code and/or sanity. It's not the
               | devil, it's a powerful tool with some downsides.
        
               | pandemic_region wrote:
               | I did not know about ErrorProne thanks, I can see this is
               | useful indeed and then Lombok would be the odd one out
               | breaking that tool. About the special IDE plugin,
               | IntelliJ has native support but yeah it needs a bit of
               | attention that's true. Overall the minor extra hassle is
               | still worth it in my world. We're not using all of the
               | annotations, but (in order of utility)
               | @AllArgsConstructor, @Data, @Getter, @Setter and the
               | occasional @Synchronized.
        
               | dwattttt wrote:
               | http://thecodelesscode.com/case/97
               | 
               | There's one of these for just about every occasion
        
               | gf000 wrote:
               | The biggest gripe is that it is _not_ a preprocessor.
               | Java has standard interfaces to properly preprocess code,
               | and they have a very thoughtful limitation of _no code
               | modification_ , it is strictly additive.
               | 
               | There are very cool libraries making use of it, e.g.
               | mapstruct.
               | 
               | Now, lombok in its current approach can't make use of it,
               | as it explicitly wants to modify the given class with its
               | annotations. This is forbidden, so they literally hack
               | into the java compiler classes themselves, modifying the
               | AST in-flight. As you can imagine, this might break at a
               | new Java update any time.
        
               | cogman10 wrote:
               | > As you can imagine, this might break at a new Java
               | update any time.
               | 
               | It's not a might. If you look at the lombok changelog
               | they have a release for nearly every new jdk version
               | because they break constantly.
               | 
               | When a project can't move up jvm versions it's very
               | frequently been because of lombok.
               | 
               | And if you look at the commit log, it's all a single dev
               | running the show. He's been doing it for years which
               | means the stability of your project is pretty dependant
               | on this one guy keeping things up to date and finding
               | work arounds to get at the jvm internals.
        
               | pests wrote:
               | Been a long time since I used Java. I checked out lombok
               | since you mentioned it. Does it really just internally
               | create all those methods without there being actual
               | source code? It seems nice but really spooky-action-at-a-
               | distance feeling. Sounds like a nightmare.
        
               | elchananHaas wrote:
               | Yes, from a language design it's ugly and the
               | implementation is convoluted. From a user perspective
               | it's awesome and enables better interfaces.
        
               | pests wrote:
               | I don't even agree with their demo video. It shows the
               | "hard" way of autogenerating settings/getters, toString,
               | hash, etc. The end result was like an additional ~80
               | boilerplate lines. I have no problem keeping those lines
               | around.. opposed to adding the lombak.jar and changing my
               | build config. I do understand the user perspective of it
               | "just working" and of course the getters/setters will
               | grow as you add fields... it just seems like such a low
               | amount of code to keep around it shouldnt be too much
               | hassle.
        
               | vbezhenar wrote:
               | It's not 80 lines. It's many thousands of lines in any
               | real project. Lines that you should keep synchronised
               | with "source of truth". People love Lombok for a reason.
               | And most developers don't really care about
               | implementation details, they believe that Lombok is "big
               | enough" to work in the foreseeable future. It worked for
               | them for years, it's supported well in Idea.
               | 
               | I, personally, avoid Lombok, specifically because I care
               | about implementation details. Because if I would have
               | wanted better Java, I'd go with Kotlin rather than this
               | hacky way of using another Java-like language. But other
               | people hold different opinions.
        
               | cogman10 wrote:
               | There are alternatives that don't rely on jvm internals
               | to accomplish their goals. They do like 90% of what
               | lombok does. Immutables and AutoValue are two examples
               | I've used that work fine.
               | 
               | The 10% lombok can do because it's peaking at internals
               | isn't valuable. I don't, for example, need an annotation
               | to create a static logger. That's dumb.
        
               | ivan_gammel wrote:
               | For me personally those thousands of lines are often
               | garbage. For example, constructors and fields generated
               | by Lombok do not validate inputs, yet failing early
               | instead of deferring the problem to client code reading
               | the value is the easiest way to fight bugs. Sometimes one
               | business method setting several values instead of
               | multiple getters is better or the only possible way to
               | preserve integrity of the state. Sometimes a field
               | reflecting internal state will be annotated to produce
               | getter and thus breaking the encapsulation. With all of
               | that Lombok encourages poor code quality by enabling
               | developers being lazy.
        
               | xxs wrote:
               | >It's many thousands of lines in any real project.
               | 
               | I dont use lombok and the latter has been actively
               | removed. If you have access to records, lombok is just
               | bad. Even if you dont have - public final fields are
               | sufficient in most case + c-tor and validation there.
               | Just dont use getters & setters.
        
               | lucumo wrote:
               | The problem isn't the first-time generating the code. The
               | problem is when objects gain fields and people forget to
               | add them to hashCode and equals (or worse, hashCode OR
               | equals). It's the kind of thing you won't notice until
               | months later when you have intermittent hard to debug
               | glitches in your system.
               | 
               | Records have reduced the advantages of Lombok by a
               | boatload. But there still are some things that can't be
               | records.
        
               | xxs wrote:
               | >add them to hashCode and equals (or worse, hashCode OR
               | equals)
               | 
               | That's a fundamental misunderstanding of hashCode, and
               | lombok makes no exception. Not all fields need to be used
               | to calculate hashCode, it makes the overall performance
               | worse in most cases. Equals is of course different,
               | however if you have an identity (i.e. database primary
               | key), only the identity should be used.
        
               | cogman10 wrote:
               | Hurray! I thought I was the only one that understood
               | this. There are two of us!
               | 
               | I've seen so many performance issues with hashcode
               | because devs will put all fields into it. Even though
               | there's an id column or even fields that imply other
               | fields. Hashing 1000 char strings when there's a UUID or
               | int field that guarantees identity is silly.
               | 
               | I think it's because devs have an preference for
               | symmetry. I see the same thing happen when they
               | preferably add setters for all fields even though they
               | aren't necessary.
        
               | xxs wrote:
               | >There are two of us!
               | 
               | Realistically, I have trained quite a few folks on my
               | own.
               | 
               | >I think it's because devs have an preference for
               | symmetry.
               | 
               | Another option is that's the default for all IDEs auto
               | gen, so few clicks/taps and it's done.
        
               | ivan_gammel wrote:
               | In Idea you have to explicitly select the fields which
               | should be included. So it's always choice of a developer
               | to misuse it.
               | 
               | My favorite question on interviews is explaining all
               | methods of class Object, including the contract and best
               | practices for equals/hashCode. Failure to answer this
               | question automatically disqualifies applicants to mid-
               | level and senior positions.
        
               | xxs wrote:
               | >all methods of class Object                 finalize()
               | is actually is a very hard mode; I'd not expect any extra
               | senior to be able to explain it properly (incl. the
               | semantics of JMM, the fact half created objects can be
               | finalized; the resurrection ability). Deprecated now, so
               | perhaps no need?       wait/notify/notifyAll - easier,
               | still require some practice, also not that useful any
               | longer; but still I'd expect to know not to use a naked
               | notify and how to properly use a loop around wait
               | clone() - it'd be a hard nut for many, and I have seen
               | more than enough implementations that straight out use
               | new XXX(); not very difficult but not intuitive
               | hashCode/equals -> hashCode being by default a random
               | number generator is sort of cool; yes they are the
               | backbone of all collection framework; also the value of
               | the not overridden hashCode() is available through
               | System.identifyHashCode()       getClass() - if included
               | anonymous classes, it might puzzle some       toString()
               | - finally something easy
               | 
               | ---
               | 
               | flip note: the standard templates for intellij could use
               | some work when it comes to the quality of hashCode;
        
               | ivan_gammel wrote:
               | > finalize() is actually is a very hard mode;
               | 
               | > Deprecated now, so perhaps no need?
               | 
               | Yes. Worth mentioning existence and "do not touch it",
               | but no need to go deep. Same with clone. The point of
               | this exercise is to demonstrate that you can use the core
               | library without shooting yourself in the foot. As for
               | wait/notify/notifyAll, I'd expect the correct usage
               | patterns from mid-level.
        
               | xxs wrote:
               | >"just working" and of course the getters/setters will
               | grow as you add fields...
               | 
               | The issue is having plainly useless getters and setter
               | (just use public fields); along with the fact that
               | mutable hashCode/equals
        
               | saghm wrote:
               | I mean, we're talking about how the best way to
               | externally modify things that are ostensibly supposed to
               | be internal and immutable, right? I feel like pretty much
               | any way of doing it is going to be spooky action at a
               | distance by definition. Modifying private internals from
               | the outside isn't something you can do with mundane-
               | action-at-close-range or whatever the alternative would
               | otherwise be called.
        
               | throwaway92422 wrote:
               | I like Lombok. The best annotation is @Builder. It makes
               | working with records much more convenient.
        
               | JanecekPetr wrote:
               | https://github.com/Randgalt/record-builder
               | 
               | The upsides are: - it generates code and does not do
               | anything funky with internals, - it has a lot of knobs if
               | you need something a little different.
               | 
               | The downside is that it does not provide you with the
               | other Lombok annotations. In practice that has been OK!
        
               | cogman10 wrote:
               | Besides this I'd also point to Immutables and AutoValue
               | which both can make builders without touching jvm
               | internals.
        
               | xxs wrote:
               | >Now, if only I could get devs to stop using lombok....
               | 
               | that's actually not too hard. It has happened to get them
               | all converted to avid opponents of lombok.
        
           | sgammon wrote:
           | It's 2025 and Guava supports JPMS now
        
             | Phelinofist wrote:
             | Android doesn't :(
        
         | sgammon wrote:
         | One could easily generate the configurations via annotations,
         | and I imagine Micronaut will do just that
        
       | nightpool wrote:
       | [Speculative optimizations] may not suffice in this case as
       | future planned optimizations may wish to rely not only on
       | immutability within the lifetime of the process, but also on the
       | immutability of fields from one run of the application to the
       | next.
       | 
       | Can someone elaborate a little more on what this means? I'm very
       | surprised to hear that this was considered a blocker important
       | enough to add all of this complicated machinery (and breaking
       | several deserialization libraries...), when I've never even heard
       | of such an optimization and can't imagine what sort of form it
       | would take
        
         | Misdicorl wrote:
         | I suppose serializing the JVM state itself to avoid the cold
         | start problem might take advantage of this?
        
           | nightpool wrote:
           | Why would that prevent the JVM from using the same
           | speculative optimization JIT with deoptimization hooks
           | approach?
        
             | Misdicorl wrote:
             | It wouldn't, but it might preclude using (future)
             | optimizations that forgo those de-optimization hooks?
        
         | pron wrote:
         | There's ongoing work, as part of Project Leyden, to cache
         | certain computations -- either performed by the user code or
         | the JVM -- from one run of the program to the next, including
         | the caching of JIT-compiled machine code. The early parts of
         | this work were discussed here:
         | https://www.morling.dev/blog/jep-483-aot-class-loading-linki...
        
         | xxs wrote:
         | This is rather old[0] but still relevant.
         | 
         |  _Surprise #2! A popular open-source framework writes to final
         | fields after object construction via generated bytecodes!...
         | This optimization of the final field is *the* main optimization
         | performed on final fields._
         | 
         | [0]
         | https://web.archive.org/web/20121016082428/http://www.azulsy...
        
       | TOGoS wrote:
       | > Application developers can avoid both current warnings and
       | future restrictions by selectively enabling the ability to mutate
       | final fields where essential.
       | 
       | /me raises hand
       | 
       | Maybe if you want to mutate a field, don't mark it `final`?
       | 
       | I know, I know, people like to pretend things are one way and
       | then hand their objects over to some horrid framework that breaks
       | all the rules, because apparently giant web of mutable spaghetti
       | is _just fine, not an anti-pattern at all_ if you let some third-
       | party bull$#!7 ORM /dependency-injection-framework-for-people-
       | who-don't-like-constructors do it.
        
       | neonsunset wrote:
       | .NET went through a similar change, blocking `static readonly`
       | fields from being accessible via private reflection.
       | Unfortunately, a lot of serializers and all sorts of meta-
       | programming libraries depend on (mutable) private reflection of
       | instance fields still so for now they are not blocked and JIT
       | cannot treat them as truly immutable, turning into JIT constants
       | the way it does so for static readonly fields. Although I guess
       | you can always make a struct and place it into a static readonly,
       | where each field could be such JIT constant (within certain
       | limits).
        
       | GauntletWizard wrote:
       | When I had the brief displeasure of working on HDFS at Facebook,
       | we took a series of customer meetings to figure out how to get
       | our oldest customers to upgrade their clusters. I was in a
       | meeting with the photos team about what their requirements were
       | and what was blocking them from upgrading, and they were very
       | frank - they asked if the upgrade preserved the internal struct
       | types associated with blocks on the disc servers. They didn't
       | actually use hdfs as a file system, they allocated 1 GB files
       | with zero replication, then used deep reflection to find the
       | extent that comprised them on the discful storage servers, then
       | built their own archival backup file system on top of that. I was
       | horrified. The some of the older hats on the team were less
       | surprised, having had some inkling of what was going on, even
       | though they clearly didn't understand the details. Others
       | considered it tantamount to sacrilege.
       | 
       | I think about this a lot. What they had built was probably
       | actually the best distributed file system within Facebook. It was
       | similarly structured to unraid, and had good availability,
       | durability, and space saving properties, but the approach to
       | engineering was just so wrong headed in my opinion that I
       | couldn't stomach it. Talking about it with other Java programmers
       | within facebook, nobody seemed to mind. Final was just a hint
       | after all.
        
         | adrianmonk wrote:
         | That reminds me of a quote from some Perl documentation[1]:
         | 
         | > _Perl does not enforce private and public parts of its
         | modules as you may have been used to in other languages like
         | C++, Ada, or Modula-17. Perl doesn 't have an infatuation with
         | enforced privacy. It would prefer that you stayed out of its
         | living room because you weren't invited, not because it has a
         | shotgun._
         | 
         | It's not exactly the same situation, but the point is, at the
         | end of the day, you need to be able to rely on the people
         | involved being willing to act reasonable. If you can't, then
         | you're going to have problems.
         | 
         | ---
         | 
         | [1] https://perldoc.perl.org/perlmodlib
        
         | dapperdrake wrote:
         | This approach is surprisingly (or unsurprisingly) one of the
         | most robust.
        
       | exabrial wrote:
       | I'm 100% onboard with this. My thought was how are they going to
       | make Serialization work, but looks like they thought of that.
       | 
       | I was trying to think of an edge case with JsonB or JAXB that
       | would be affected by this... but generally those frameworks have
       | told you for quite awhile not to do stupid stuff like:
       | 
       | ``` @Getter public class HelloMessage { @JsonbProperty private
       | final String helloMessage; } ```
       | 
       | I can't think of any frameworks offhand that do this.
        
         | hyperpape wrote:
         | Brian Goetz, chief architect of Java, once posted a "what they
         | think I do" vs. "what I actually" do tweet. If I remember
         | correctly, 25% - 50% of the "what I actually do" category was
         | something like "get angry at serialization."
         | 
         | So I think it's safe to say "what about serialization?" is
         | always going to be asked.
        
           | exabrial wrote:
           | Serialization is unfortunately important though... want to
           | suspend a program and resume it later, or transfer it over a
           | network, etc. The real world is kinda a let down.
        
             | esprehn wrote:
             | That's a hydration problem though, it doesn't require
             | magically serializing the running state like java
             | Serializable wants to. It just needs a way to produce
             | minimal inputs to then reconstitute the state again on the
             | other side.
        
           | dstine wrote:
           | In this 2014 talk, Brian shows a slide in which he
           | characterizes a visible fraction of his job as "regretting
           | serialization" (somewhat tongue-in-cheek).
           | 
           | https://www.youtube.com/watch?v=2y5Pv4yN0b0&t=930s
           | 
           | Link is to the start of a sequence of three slides, the third
           | of which is the slide in question.
           | 
           | For a more recent update on serialization, watch this talk
           | "Serialization: A New Hope":
           | https://www.youtube.com/watch?v=mIbA2ymCWDs
        
       | xxs wrote:
       | Around 19 year late, still better than never.
        
       | ars wrote:
       | Does this mean I should start marking my variables (and function
       | parameters) with Final?
       | 
       | Up till now I always assumed the compiler would figure out on its
       | own which variables were final, and optimize as needed. But this
       | JEP makes it seem like there are optimizations that only happen
       | if you manually mark the variable.
        
         | pron wrote:
         | No, this JEP only talks about _fields_ ; it has no impact
         | whatsoever on locals. There's no positive or negative impact on
         | performance when making locals final or not.
        
       | mberning wrote:
       | If they implement this in a way similar to the package visibility
       | changes your list of JVM args is about to explode in order to
       | support legacy apps.
        
       | user wrote:
       | This comment is non-sensical.
        
       | theanonymousone wrote:
       | If there was a r/nottheonion for tech :D
       | 
       | Jokes aside, I thought the ability to mutate final fields was
       | already removed/restricted after Java 17 :/
        
       | satyanash wrote:
       | > _Prepare to make final mean final_
       | 
       | missed opportunity to call it "final final"
        
       ___________________________________________________________________
       (page generated 2025-04-01 23:02 UTC)