JEP draft: Prepare to make final mean final

Immutability and “const-ness” across languages

  • Several comments wish Java made final (or val) the default and discouraged mutability.
  • People compare Java unfavorably to C++ const and especially Rust, where mutability is explicit (&mut) and visible at call sites, making reasoning about side effects easier.
  • Others warn that overusing const/final often masks poor design and can make code hard to evolve, leading to hacks like casting away const.
  • D’s transitive const is cited as powerful but “weird” and hard to get right; some prefer using const very sparingly.

C/C++ const vs Java final

  • C/C++ const is criticized as mostly documentation plus extra compiler errors, undermined by const_cast and confusing rules about when UB occurs.
  • Some praise Java for being willing to tighten semantics, unlike C++ where const-based optimization is limited by these escape hatches.

What the JEP changes about final and reflection

  • The core change: reflective mutation of final instance fields (via setAccessible/Field::set) will be restricted or require explicit opt-in.
  • There are JVM flags such as --illegal-final-final-mutation=deny to turn violations into hard errors.
  • Static finals, record fields, and hidden classes already behave as truly immutable; this JEP extends that direction.
  • The aim is “integrity by default”: libraries shouldn’t be able to secretly rewrite other code’s invariants.

Impact on serialization, frameworks, and tests

  • Many frameworks (GSON, JAXB, mocking libraries, class generators, Lombok, Spring, etc.) rely on reflective access; concern that this will become another “module system / --add-opens” situation.
  • Counterpoint: most such libraries don’t need to set final fields; private access alone is enough.
  • Java serialization and similar libraries get special escape hatches via sun.reflect.ReflectionFactory and Serializable, though that doesn’t cover all JSON-style cases; some see that as insufficient.
  • Ideas raised: annotations to mark “final-mutatable” classes, or a dedicated “test mode” flag. Others push back: global easy flags encourage misuse; better to have tools/builds assemble precise options.

Optimization and Project Leyden motivations

  • Supporters stress this is about correctness and enabling stronger optimizations (e.g., constant folding of truly-final fields), not just micro-speedups.
  • Future Leyden-style caching of computations and JIT code across runs may rely on knowing fields never change across executions, not just within one run.
  • Skeptics argue the JVM already does speculative optimization and deoptimization; proponents reply that deopt is expensive and pervasive reflective mutation prevents many optimizations entirely.

Java evolution, tooling, and ecosystem tangents

  • Some see this as another painful but ultimately successful step, like JPMS: early breakage, but smoother upgrades later and fewer internals abuses.
  • Others complain Java’s evolution (modules, integrity flags, complex build tools) feels heavy compared to ecosystems like Rust’s Cargo or Go’s tooling.
  • Long tangent on Lombok: loved for boilerplate reduction, but criticized as a brittle hack on compiler internals and a frequent blocker for JDK upgrades; alternatives (records, annotation processors, other libs) are mentioned.

Security, integrity, and “developer agency”

  • One side frames this as protecting applications from supply-chain issues and misbehaving libraries that use reflection/JNI to bypass invariants.
  • The opposing view: setAccessible(true) is explicitly a “I know what I’m doing” escape hatch; restricting it further undermines extensibility and controlled monkey-patching.
  • Pro-change commenters answer that nothing is outright impossible: you can still bypass final, but it should be visible, deliberate, and come with clear configuration, not happen silently inside libraries.