From archie.cobbs at gmail.com Tue Jan 13 22:56:51 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 13 Jan 2026 16:56:51 -0600 Subject: Amber features 2026 In-Reply-To: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> References: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> Message-ID: [ This is in reply to https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004306.html on amber-spec-experts. ] Hi Gavin, The "Pattern Assignment" and "Constant Patterns" ideas sound good to me. The "Pattern Assignment" idea is a natural way to extend pattern matching, which is motivated by the frequent need to declare variables to access the "variable components" in an instance of some object type. My question is: Wouldn't the same motivation apply to generic type parameters as well? Here's a trivial example. Suppose we have this class: public record Property(T value) { boolean has(T t) { return t == value; } } and we want to write a method to check that a list of Property's are well-behaved. Here's what we'd LIKE to do, which is use "Pattern Assignment" for the generic type variable: public void verifyHas(List> properties) { for (Property element : properties) { Property property = element; T value = property.value(); assert property.has(value); // no cast needed here! } } but here's what we HAVE to do today to avoid the unchecked cast: public void verifyHas(List> properties) { for (Property property : properties) { verifyHas(property); } } // Ugh. This method exists solely to declare so we can avoid an unchecked cast private void verifyHas(Property property) { T value = property.value(); assert property.has(value); } If we're going to add "Pattern Assignment" it seems like it would be reasonable for generic type variables to also benefit. Cheers, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 14 02:33:12 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 13 Jan 2026 21:33:12 -0500 Subject: Amber features 2026 In-Reply-To: References: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> Message-ID: While I totally understand why you would want to do that, I don't see the connection between pattern assignment and extending the type system to permit denotation of quantified or existential types?? I don't see any patterns in your example, only type manipulation. What you're saying is that you'd like a conversion from Foo to `\exist T . Foo`.? This is understandable, as wildcards are basically existentials already, and is just as safe as the the trick of calling an out-of-line generic method. But I just don't see the connection with pattern assignment?? It seems like what you really want is "generic variables", to go along with generic methods and classes? On 1/13/2026 5:56 PM, Archie Cobbs wrote: > [ This is in reply to > https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004306.html > on amber-spec-experts. ] > > Hi Gavin, > > The "Pattern Assignment" and "Constant Patterns" ideas sound?good to > me. The "Pattern Assignment" idea?is a natural way to extend pattern > matching, which is motivated by the frequent need to declare variables > to access the "variable components" in an instance of some object type. > > My question is: Wouldn't the same motivation apply to generic type > parameters as well? > > Here's a trivial example. Suppose we have this class: > > ? ? public record Property(T value) { > ? ? ? ? boolean has(T t) { > ? ? ? ? ? ? return t == value; > ? ? ? ? } > ? ? } > > and we want to write a method to check that a list of Property's are > well-behaved. > > Here's what we'd LIKE to do, which is use "Pattern Assignment" for the > generic type variable: > > ? ? public void verifyHas(List> properties) { > ? ? ? ? for (Property element : properties) { > Property property = element; > ? ? ? ? ? ? T value = property.value(); > ? ? ? ? ? ? assert property.has(value);? // no cast needed here! > ? ? ? ? } > ? ? } > > but here's what we HAVE to do today to avoid the unchecked cast: > > ? ? public void verifyHas(List> properties) { > ? ? ? ? for (Property property : properties) { > ? ? ? ? ? ? verifyHas(property); > ? ? ? ? } > ? ? } > > ? ? // Ugh. This method exists solely to declare so we can avoid > an unchecked cast > ? ? private void verifyHas(Property property) { > ? ? ? ? T value = property.value(); > ? ? ? ? assert property.has(value); > ? ? } > > If we're going to add?"Pattern Assignment" it seems like it would be > reasonable for generic type variables to also benefit. > > Cheers, > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From ganapathibasamsetti at gmail.com Wed Jan 14 03:25:50 2026 From: ganapathibasamsetti at gmail.com (Ganapathi Vara Prasad) Date: Wed, 14 Jan 2026 08:55:50 +0530 Subject: Data Oriented Programming, Beyond Records Message-ID: Hello Brian, Thank you for thinking on this feature. I want to better understand the thought process behind marking all fields that are part of the component state instead of only the derived fields. Something like this: ``` class Point(int x, int y) { private final int x; private final int y; private final derived double norm; Point { norm = Math.hypot(x, y); } public double norm() { return norm; } // derived implementation of x and y accessors // derived implementation of equals, hashCode, toString } ``` -------------- next part -------------- An HTML attachment was scrubbed... URL: From ganapathibasamsetti at gmail.com Wed Jan 14 03:40:29 2026 From: ganapathibasamsetti at gmail.com (Ganapathi Vara Prasad) Date: Wed, 14 Jan 2026 09:10:29 +0530 Subject: Data Oriented Programming, Beyond Records In-Reply-To: References: Message-ID: Corrected Brian's email address. On Wed, 14 Jan, 2026, 8:55?am Ganapathi Vara Prasad, < ganapathibasamsetti at gmail.com> wrote: > Hello Brian, > > Thank you for thinking on this feature. I want to better understand the > thought process behind marking all fields that are part of the component > state instead of only the derived fields. Something like this: > > ``` > > class Point(int x, int y) { > private final int x; > private final int y; > private final derived double norm; > > Point { > norm = Math.hypot(x, y); > } > > public double norm() { return norm; } > > // derived implementation of x and y accessors > // derived implementation of equals, hashCode, toString > } > ``` > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 14 03:43:57 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 13 Jan 2026 22:43:57 -0500 Subject: Data Oriented Programming, Beyond Records In-Reply-To: References: Message-ID: <0c047449-c92d-4b0a-9ccb-091a369741f4@oracle.com> First, a gentle reminder that I did ask "please, let's not discuss syntax."? It is way too early for that; we haven't even had a discussion on the value of the ideas yet.? But people can't help but obsess on syntax, so I'll answer, but please let's let this thread end here. Yes, we considered matching on name and type only.? There is nothing about that approach that makes it unworkable, but it is less reliable, and subjectively, seems to be more likely to feel "magic" or "action at a distance" to the Java developers we showed this to.? By comparison, the overhead of the `component` modifier is small; it is purely horizontal rather than vertical, and admits no question about which fields are component fields or not.? It also admits greater flexibility for users (under the implicit approach, we'd almost certainly want to error out if the names matched but the types didn't; with the explicit version, we can accept examples like the AlmostRecord in the writeup.) Basically: the value of the clarity seems to outweigh the value of the concision.? )(And, as you point out, a new modifier would still be needed for "not component" in that case.) On 1/13/2026 10:25 PM, Ganapathi Vara Prasad wrote: > Hello Brian, > > Thank you for thinking on this feature. I want to better understand > the thought?process behind marking all fields that are part of the > component state instead of only the derived fields. Something like this: > > ``` > class Point(int x, int y) { > ? ? private final int x; > ? ? private final int y; > ? ? private final derived double norm; > > ? ? Point { > ? ? ? ? norm = Math.hypot(x, y); > ? ? } > > ? ? public double norm() { return norm; } > > ? ? // derived implementation of x and y accessors > ? ? // derived implementation of equals, hashCode, toString > } > ``` -------------- next part -------------- An HTML attachment was scrubbed... URL: From ganapathibasamsetti at gmail.com Wed Jan 14 04:04:25 2026 From: ganapathibasamsetti at gmail.com (Ganapathi Vara Prasad) Date: Wed, 14 Jan 2026 09:34:25 +0530 Subject: Data Oriented Programming, Beyond Records In-Reply-To: <0c047449-c92d-4b0a-9ccb-091a369741f4@oracle.com> References: <0c047449-c92d-4b0a-9ccb-091a369741f4@oracle.com> Message-ID: Hi Brian, I might have worded question incorrectly, but it was about the idea of marking (which we need) on component vs derived fields. Why would we choose one over the other irrespective of the syntax used to achieve that? I still need to understand your response, but thanks for the response. On Wed, 14 Jan, 2026, 9:14?am Brian Goetz, wrote: > First, a gentle reminder that I did ask "please, let's not discuss > syntax." It is way too early for that; we haven't even had a discussion on > the value of the ideas yet. But people can't help but obsess on syntax, so > I'll answer, but please let's let this thread end here. > > Yes, we considered matching on name and type only. There is nothing about > that approach that makes it unworkable, but it is less reliable, and > subjectively, seems to be more likely to feel "magic" or "action at a > distance" to the Java developers we showed this to. By comparison, the > overhead of the `component` modifier is small; it is purely horizontal > rather than vertical, and admits no question about which fields are > component fields or not. It also admits greater flexibility for users > (under the implicit approach, we'd almost certainly want to error out if > the names matched but the types didn't; with the explicit version, we can > accept examples like the AlmostRecord in the writeup.) > > Basically: the value of the clarity seems to outweigh the value of the > concision. )(And, as you point out, a new modifier would still be needed > for "not component" in that case.) > > > On 1/13/2026 10:25 PM, Ganapathi Vara Prasad wrote: > > Hello Brian, > > Thank you for thinking on this feature. I want to better understand the > thought process behind marking all fields that are part of the component > state instead of only the derived fields. Something like this: > > ``` > > class Point(int x, int y) { > private final int x; > private final int y; > private final derived double norm; > > Point { > norm = Math.hypot(x, y); > } > > public double norm() { return norm; } > > // derived implementation of x and y accessors > // derived implementation of equals, hashCode, toString > } > ``` > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Wed Jan 14 04:15:01 2026 From: john.r.rose at oracle.com (John Rose) Date: Tue, 13 Jan 2026 20:15:01 -0800 Subject: Amber features 2026 In-Reply-To: References: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> Message-ID: <982EF2DB-F7C9-48CC-AD3F-EF5063535A97@oracle.com> On 13 Jan 2026, at 18:33, Brian Goetz wrote: > While I totally understand why you would want to do that, I don't see > the connection between pattern assignment and extending the type > system to permit denotation of quantified or existential types?? I > don't see any patterns in your example, only type manipulation. > > What you're saying is that you'd like a conversion from Foo to > `\exist T . > Foo`.? This is understandable, as wildcards are basically > existentials already, and is just as safe as the the trick of calling > an out-of-line generic method. > > But I just don't see the connection with pattern assignment?? It > seems like what you really want is "generic variables", to go along > with generic methods and classes? I?ve wanted this sort of thing from time to time. I think of the ask as "local type variables", where the type variable is local to a block (tighter control than local to method or class/interface). But it would only be set via inference, not explicitly; in that it would be a narrower feature than type variables on methods or classes. The two other type variable occurrences (method, class) offer explicit specification as well as inference. But there is no occasion for explicit specification of a local type. If you have a type you want to use explicitly, you just declare the local with that type. Other kinds of variables (fields, not locals) also do not benefit from having their types inferred, for the same reason that the /var/ keyword is only accepted on locals. (Inferring a type for var on an API point is dangerously obscure, not worth the concision.) So, local type vars. The form Archie mentions is also the one that I have wondered about, these many years. ``` var x1a = foo(); // x1a has type inferred from foo() T1 x1b = foo(); // so does x1b; now it is denotable as T1 T1 x1c; // and now x1c has the same type too { x1a = x1b; x1b = x1c; x1c = x1a; } // types are the same Map x2 = bar(); // capturing g-type structure T3 x3a = baz(); List x3b = bat(); // checks result of baz is list of what x3a is T4 x4; // ERROR; must have an initializer to drive inference ``` This might allow some new types to become denotable, so maybe it?s dangerous. Maybe there is some useful sanitization move that could apply to type variables captured this way, as with var. But var definitely can capture non-denotables, despite sanitization. ``` var o1 = new Object() { int f; }; o1.f++; // field WHOOZIT.f o2a = new Object() { long f; static int Q; }; T2 o2b = o2a; // rather odd o2a.Q++; // static variable reference (deprecated syntax) o2b.Q++; // again? T2.Q++; // ERROR; unless T2 is a type alias instead of a type var ``` Oddly, the range of variation of such a type var is very narrow, since it can be driven only from one use site, the initialization. But it?s still a type var, if you squint, since the thing coming out of the initializer expression can have type vars mixed into it. Feature priority? Very low! It?s a "filling in the corners" move. No known important use cases. The use case I had was avoiding refactoring to a private generic method, because the body wanted to access some local vars in an enclosing scope. In the end, I think I just boxed the local vars in an array and moved on with the generic helper method. It did make the code more obscure, so having a local type var would have been an aid in readability. But it?s rare to want to name that type, and I can?t recall why I needed to. In other words, I am not asking for a JEP or RFE for this. Just laying out the case FTR, in case a use comes up later. Maybe the balance shifts if/when we get reified generics, since then there will be "more to capture". There, got it all off my chest. Thanks Archie for fellow-traveling. Now, back to 2026. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Jan 14 08:39:55 2026 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 14 Jan 2026 09:39:55 +0100 (CET) Subject: Amber features 2026 In-Reply-To: <982EF2DB-F7C9-48CC-AD3F-EF5063535A97@oracle.com> References: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> <982EF2DB-F7C9-48CC-AD3F-EF5063535A97@oracle.com> Message-ID: <957379390.14745646.1768379995105.JavaMail.zimbra@univ-eiffel.fr> > From: "John Rose" > To: "Brian Goetz" , "Archie Cobbs" > > Cc: "Gavin Bierman" , "amber-dev" > > Sent: Wednesday, January 14, 2026 5:15:01 AM > Subject: Re: Amber features 2026 > On 13 Jan 2026, at 18:33, Brian Goetz wrote: >> While I totally understand why you would want to do that, I don't see the >> connection between pattern assignment and extending the type system to permit >> denotation of quantified or existential types? I don't see any patterns in your >> example, only type manipulation. >> What you're saying is that you'd like a conversion from Foo to `\exist T . >> Foo`. This is understandable, as wildcards are basically existentials >> already, and is just as safe as the the trick of calling an out-of-line generic >> method. >> But I just don't see the connection with pattern assignment? It seems like what >> you really want is "generic variables", to go along with generic methods and >> classes? > I?ve wanted this sort of thing from time to time. I think of the ask > as "local type variables", where the type variable is local to a block > (tighter control than local to method or class/interface). But it > would only be set via inference, not explicitly; in that it would > be a narrower feature than type variables on methods or classes. > The two other type variable occurrences (method, class) offer > explicit specification as well as inference. But there is no > occasion for explicit specification of a local type. If you > have a type you want to use explicitly, you just declare the > local with that type. > Other kinds of variables (fields, not locals) also do not benefit > from having their types inferred, for the same reason that the /var/ > keyword is only accepted on locals. (Inferring a type for var on > an API point is dangerously obscure, not worth the concision.) > So, local type vars. The form Archie mentions is also the one > that I have wondered about, these many years. > var x1a = foo(); // x1a has type inferred from foo() > T1 x1b = foo(); // so does x1b; now it is denotable as T1 > T1 x1c; // and now x1c has the same type too > { x1a = x1b; x1b = x1c; x1c = x1a; } // types are the same > Map x2 = bar(); // capturing g-type structure > T3 x3a = baz(); > List x3b = bat(); // checks result of baz is list of what x3a is > T4 x4; // ERROR; must have an initializer to drive inference > This might allow some new types to become denotable, so maybe > it?s dangerous. Maybe there is some useful sanitization move > that could apply to type variables captured this way, as with var. > But var definitely can capture non-denotables, despite sanitization. > var o1 = new Object() { int f; }; > o1.f++; // field WHOOZIT.f > o2a = new Object() { long f; static int Q; }; > T2 o2b = o2a; // rather odd > o2a.Q++; // static variable reference (deprecated syntax) > o2b.Q++; // again? > T2.Q++; // ERROR; unless T2 is a type alias instead of a type var > Oddly, the range of variation of such a type var is very narrow, > since it can be driven only from one use site, the initialization. > But it?s still a type var, if you squint, since the thing coming > out of the initializer expression can have type vars mixed into it. > Feature priority? Very low! It?s a "filling in the corners" move. > No known important use cases. The use case I had was avoiding > refactoring to a private generic method, because the body wanted > to access some local vars in an enclosing scope. In the end, > I think I just boxed the local vars in an array and moved on > with the generic helper method. It did make the code more > obscure, so having a local type var would have been an aid > in readability. But it?s rare to want to name that type, > and I can?t recall why I needed to. > In other words, I am not asking for a JEP or RFE for this. > Just laying out the case FTR, in case a use comes up later. > Maybe the balance shifts if/when we get reified generics, > since then there will be "more to capture". > There, got it all off my chest. Thanks Archie for fellow-traveling. > Now, back to 2026. For fields, i think I would prefer to have a way to denote the type of null and bottom/nothing. class A { List list = List.of(); // can be List list = List.of(); } Inside a method, you sometime need to access a static field (e.g. you can use it as a JIT "anchor") void main() { var o1 = new Object() { int f; static int Q; }; o1.Q++; // can be record T1() { static int Q; } // the compiler adds a package private constructor T1.Q++; // or enum T2 { static int Q; } // the compiler adds values()/valueOf() T2.Q++; // this does not compile // the keyword "final" is allowed but "static" is not static final class T3 { private T3() {} static int Q; } } another example is the class initializer idiom class DBFacade { public static DB getLazyDB() { final class DBInit { private DBInit() {} private static final DB INSTANCE = new DB(); } return DBInit.INSTANCE; } } Here the class DBInit is "static" because it is defined in a static context (inside the static method). So the two missing pieces are - being able to denote the type of null and/or nothing, - being able to declare a local class static * > ? John R?mi * also allow private classes in interface which is another hole to plug. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Jan 14 16:31:53 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 14 Jan 2026 10:31:53 -0600 Subject: Amber features 2026 In-Reply-To: References: <1C8344A8-B712-422D-9F66-FB4ED89373FC@oracle.com> Message-ID: Hi Brian, On Tue, Jan 13, 2026 at 8:33?PM Brian Goetz wrote: > While I totally understand why you would want to do that, I don't see the > connection between pattern assignment and extending the type system to > permit denotation of quantified or existential types? > Well it's kind of a loose "connection" I guess :) I'm just pointing out that the two features have a common motivation: the ability to declare a variable that is initialized from some existing declared variable of an object. This is handy! This idea of "local type vars" has come up before and been rejected. That's fine, but if the underlying motivations are the same, then it seems like we are being inconsistent by promoting one feature while ignoring the other. They are both useful, and for the same reason (roughly speaking), so why not complete the picture while we're mucking with the language syntax? Admittedly, I'm asking not based on some conceptual language design principle, but simply because this particular omission has always bugged me and it seems relatively easy to fix without too much ugliness (maybe; to be determined). -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Mon Jan 19 22:10:49 2026 From: duke at openjdk.org (Nicolai Parlog) Date: Mon, 19 Jan 2026 22:10:49 GMT Subject: [amber-docs] RFR: Capitalize article titles according to Chicago style Message-ID: Capitalize article titles according to Chicago style ------------- Commit messages: - Capitalize article titles according to Chicago style Changes: https://git.openjdk.org/amber-docs/pull/28/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=28&range=00 Stats: 2 lines in 2 files changed: 0 ins; 0 del; 2 mod Patch: https://git.openjdk.org/amber-docs/pull/28.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/28/head:pull/28 PR: https://git.openjdk.org/amber-docs/pull/28 From briangoetz at openjdk.org Mon Jan 19 22:10:50 2026 From: briangoetz at openjdk.org (Brian Goetz) Date: Mon, 19 Jan 2026 22:10:50 GMT Subject: [amber-docs] RFR: Capitalize article titles according to Chicago style In-Reply-To: References: Message-ID: On Sun, 18 Jan 2026 21:24:19 GMT, Nicolai Parlog wrote: > Capitalize article titles according to Chicago style Marked as reviewed by briangoetz (Lead). ------------- PR Review: https://git.openjdk.org/amber-docs/pull/28#pullrequestreview-3675781159 From briangoetz at openjdk.org Mon Jan 19 22:11:58 2026 From: briangoetz at openjdk.org (Brian Goetz) Date: Mon, 19 Jan 2026 22:11:58 GMT Subject: [amber-docs] RFR: Add "Data-Oriented Programming for Java: Beyond Records" In-Reply-To: References: Message-ID: On Sun, 18 Jan 2026 21:25:00 GMT, Nicolai Parlog wrote: > Add "Data-Oriented Programming for Java: Beyond Records" Marked as reviewed by briangoetz (Lead). ------------- PR Review: https://git.openjdk.org/amber-docs/pull/29#pullrequestreview-3675781465 From duke at openjdk.org Mon Jan 19 22:11:58 2026 From: duke at openjdk.org (Nicolai Parlog) Date: Mon, 19 Jan 2026 22:11:58 GMT Subject: [amber-docs] RFR: Add "Data-Oriented Programming for Java: Beyond Records" Message-ID: Add "Data-Oriented Programming for Java: Beyond Records" ------------- Commit messages: - Add "Data-Oriented Programming for Java: Beyond Records" Changes: https://git.openjdk.org/amber-docs/pull/29/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=29&range=00 Stats: 689 lines in 2 files changed: 689 ins; 0 del; 0 mod Patch: https://git.openjdk.org/amber-docs/pull/29.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/29/head:pull/29 PR: https://git.openjdk.org/amber-docs/pull/29 From nlisker at openjdk.org Mon Jan 19 22:37:19 2026 From: nlisker at openjdk.org (Nir Lisker) Date: Mon, 19 Jan 2026 22:37:19 GMT Subject: [amber-docs] RFR: Add "Data-Oriented Programming for Java: Beyond Records" In-Reply-To: References: Message-ID: On Sun, 18 Jan 2026 21:25:00 GMT, Nicolai Parlog wrote: > Add "Data-Oriented Programming for Java: Beyond Records" site/design-notes/beyond-records.md line 117: > 115: This is more concise, less error-prone, and easier to read: > 116: > 117: ```{.java} I don't see this text block rendered with Java syntax highlights. I usually use just the language name without `{. }`. ------------- PR Review Comment: https://git.openjdk.org/amber-docs/pull/29#discussion_r2706234767 From amazingcodewithus at gmail.com Fri Jan 23 12:42:21 2026 From: amazingcodewithus at gmail.com (Amazing Code) Date: Fri, 23 Jan 2026 18:12:21 +0530 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references Message-ID: I am writing to propose a language enhancement regarding the handling of static member access in Java. *Issue* Java currently permits static fields and methods to be accessed through object references, despite static members belonging strictly to the class. This behavior is often misleading and can create confusion, especially in large codebases or among less-experienced developers. Example: MyClass obj = new MyClass(); obj.staticMethod(); // Currently allowed, but confusing *Proposed Enhancement* I request consideration of a change that *disallows access to static members via object references*, enforcing access exclusively through the class name. This would convert the current warning into a *compile-time error*. *Rationale* - Prevents misconceptions about instance vs. class-level behavior - Improves code clarity and consistency - Reduces maintenance complexity in enterprise applications - Encourages best practices already recommended by the community *Suggested Requirements* 1. Compiler should produce an error when static members are accessed through object references. 2. Error messages should explicitly guide developers to use class-based access. 3. Rules should apply to static fields, static methods, and static nested types. 4. Optionally, provide a compiler flag for backward compatibility during migration. *Conclusion* Restricting object-based access to static members would strengthen language clarity and help eliminate a common source of misunderstanding. I kindly request your consideration of this enhancement for future Java releases. Thank you for your time and continued work on the Java platform. Sincerely, *Kamlesh Kohli* -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Jan 23 12:56:42 2026 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 23 Jan 2026 13:56:42 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Hello! By the way, this "feature" complicates the static analysis, so I'd like if we get rid of it. There's even a bug in IntelliJ IDEA, which I don't want to fix. Try the following class: class Test { static void test() { System.out.println("Static"); } static void process(Test test) { test.test(); } public static void main(String[] args) { process(null); } } There's a false-positive warning at `process(null)`: Passing 'null' argument to parameter annotated as @NotNull, despite the code runs perfectly. IntelliJ IDEA assumes that if you dereference the `test` variable via `test.test()` call, then `test` must not be null, so passing `null` to the `process` method should be disallowed. At this stage of analysis, we cannot spend time on resolving the `test()` call and checking whether it's static or not, so we just assume that nobody in their sane mind calls static methods on instance qualifiers. By the way, there's a warning already. It's just turned off by default. C:\Temp>javac.exe -Xlint:static Test.java Test.java:7: warning: [static] static method should be qualified by type name, Test, instead of by an expression test.test(); ^ 1 warning With best regards, Tagir Valeev. On Fri, Jan 23, 2026 at 1:43?PM Amazing Code wrote: > I am writing to propose a language enhancement regarding the handling of > static member access in Java. > *Issue* > > Java currently permits static fields and methods to be accessed through > object references, despite static members belonging strictly to the class. > This behavior is often misleading and can create confusion, especially in > large codebases or among less-experienced developers. > > Example: > > MyClass obj = new MyClass(); > obj.staticMethod(); // Currently allowed, but confusing > > *Proposed Enhancement* > > I request consideration of a change that *disallows access to static > members via object references*, enforcing access exclusively through the > class name. This would convert the current warning into a *compile-time > error*. > *Rationale* > > - > > Prevents misconceptions about instance vs. class-level behavior > - > > Improves code clarity and consistency > - > > Reduces maintenance complexity in enterprise applications > - > > Encourages best practices already recommended by the community > > *Suggested Requirements* > > 1. > > Compiler should produce an error when static members are accessed > through object references. > 2. > > Error messages should explicitly guide developers to use class-based > access. > 3. > > Rules should apply to static fields, static methods, and static nested > types. > 4. > > Optionally, provide a compiler flag for backward compatibility during > migration. > > *Conclusion* > > Restricting object-based access to static members would strengthen > language clarity and help eliminate a common source of misunderstanding. I > kindly request your consideration of this enhancement for future Java > releases. > > Thank you for your time and continued work on the Java platform. > > Sincerely, > *Kamlesh Kohli* > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jan 23 13:15:18 2026 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 23 Jan 2026 08:15:18 -0500 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: I completely disagree with this proposal. The warning is good enough. Maybe more attention to the warnings, or better documentation on what they are. It's not super obvious where to find the documentation for them, but Hannes and friends are already working on that. At best, I might consider turning the warning on by default. But that's about it. On Fri, Jan 23, 2026 at 7:57?AM Tagir Valeev wrote: > Hello! > > By the way, this "feature" complicates the static analysis, so I'd like if > we get rid of it. There's even a bug in IntelliJ IDEA, which I don't want > to fix. Try the following class: > > class Test { > static void test() { > System.out.println("Static"); > } > > static void process(Test test) { > test.test(); > } > > public static void main(String[] args) { > process(null); > } > } > > There's a false-positive warning at `process(null)`: Passing 'null' > argument to parameter annotated as @NotNull, despite the code runs > perfectly. IntelliJ IDEA assumes that if you dereference the `test` > variable via `test.test()` call, then `test` must not be null, so passing > `null` to the `process` method should be disallowed. At this stage of > analysis, we cannot spend time on resolving the `test()` call and checking > whether it's static or not, so we just assume that nobody in their sane > mind calls static methods on instance qualifiers. > > By the way, there's a warning already. It's just turned off by default. > > C:\Temp>javac.exe -Xlint:static Test.java > Test.java:7: warning: [static] static method should be qualified by type > name, Test, instead of by an expression > test.test(); > ^ > 1 warning > > With best regards, > Tagir Valeev. > > On Fri, Jan 23, 2026 at 1:43?PM Amazing Code > wrote: > >> I am writing to propose a language enhancement regarding the handling of >> static member access in Java. >> *Issue* >> >> Java currently permits static fields and methods to be accessed through >> object references, despite static members belonging strictly to the class. >> This behavior is often misleading and can create confusion, especially in >> large codebases or among less-experienced developers. >> >> Example: >> >> MyClass obj = new MyClass(); >> obj.staticMethod(); // Currently allowed, but confusing >> >> *Proposed Enhancement* >> >> I request consideration of a change that *disallows access to static >> members via object references*, enforcing access exclusively through the >> class name. This would convert the current warning into a *compile-time >> error*. >> *Rationale* >> >> - >> >> Prevents misconceptions about instance vs. class-level behavior >> - >> >> Improves code clarity and consistency >> - >> >> Reduces maintenance complexity in enterprise applications >> - >> >> Encourages best practices already recommended by the community >> >> *Suggested Requirements* >> >> 1. >> >> Compiler should produce an error when static members are accessed >> through object references. >> 2. >> >> Error messages should explicitly guide developers to use class-based >> access. >> 3. >> >> Rules should apply to static fields, static methods, and static >> nested types. >> 4. >> >> Optionally, provide a compiler flag for backward compatibility during >> migration. >> >> *Conclusion* >> >> Restricting object-based access to static members would strengthen >> language clarity and help eliminate a common source of misunderstanding. I >> kindly request your consideration of this enhancement for future Java >> releases. >> >> Thank you for your time and continued work on the Java platform. >> >> Sincerely, >> *Kamlesh Kohli* >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Fri Jan 23 13:39:35 2026 From: redio.development at gmail.com (Red IO) Date: Fri, 23 Jan 2026 14:39:35 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: I understand your idea. But I think that's exactly what the warning is for. It suggests people to not access static data through objects. Making it an hard error would likely break many code bases requiring massive migrations without any real benefits. Treating warnings as errors and annotating necessary warnings with suppress warnings and a comment should be best practice anyway. Great regards RedIODev On Fri, Jan 23, 2026, 13:42 Amazing Code wrote: > I am writing to propose a language enhancement regarding the handling of > static member access in Java. > *Issue* > > Java currently permits static fields and methods to be accessed through > object references, despite static members belonging strictly to the class. > This behavior is often misleading and can create confusion, especially in > large codebases or among less-experienced developers. > > Example: > > MyClass obj = new MyClass(); > obj.staticMethod(); // Currently allowed, but confusing > > *Proposed Enhancement* > > I request consideration of a change that *disallows access to static > members via object references*, enforcing access exclusively through the > class name. This would convert the current warning into a *compile-time > error*. > *Rationale* > > - > > Prevents misconceptions about instance vs. class-level behavior > - > > Improves code clarity and consistency > - > > Reduces maintenance complexity in enterprise applications > - > > Encourages best practices already recommended by the community > > *Suggested Requirements* > > 1. > > Compiler should produce an error when static members are accessed > through object references. > 2. > > Error messages should explicitly guide developers to use class-based > access. > 3. > > Rules should apply to static fields, static methods, and static nested > types. > 4. > > Optionally, provide a compiler flag for backward compatibility during > migration. > > *Conclusion* > > Restricting object-based access to static members would strengthen > language clarity and help eliminate a common source of misunderstanding. I > kindly request your consideration of this enhancement for future Java > releases. > > Thank you for your time and continued work on the Java platform. > > Sincerely, > *Kamlesh Kohli* > -------------- next part -------------- An HTML attachment was scrubbed... URL: From contact at mechite.com Fri Jan 23 15:06:04 2026 From: contact at mechite.com (Mahied Maruf) Date: Fri, 23 Jan 2026 15:06:04 +0000 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references Message-ID: I would just like to also append abstract class static method inheritance. Currently, the following patterns are quite common: /*package-private*/ abstract class A { public static void example() { } } public class B extends A { ... } B.example(); and I consider them to be a good and useful element of the language design. Specifically the case that was demonstrated here of accessing static methods against a local variable or method argument instance, that does stick out to look like an antipattern but I would still prefer to leave it as a warning. If in the future it actually becomes anomalous to determine what method was called, it makes more sense to prevent this case. Potentially breaking programs _only_ without the motivation behind introducing something (e.g. say when `_` was reserved when lambdas were introduced) is not very smart and it's better to keep it as a documented footgun. > David Alayachew said: > I might consider turning the warning on by default +1 for this. It's a strong warning by default in most IDEs as well. > Tagir Valeev said: > By the way, this "feature" complicates the static analysis Then improve your software. Java is a more difficult language to analyze and I think the incompetence of a piece of software should not be the motivation for the feature. Is it really that difficult to analyze? > There's a false-positive warning at `process(null)` I would say that this is one case where the nullability analysis should just be improved, but I also think it's not a false-positive warning and could genuinely be useful - you generally don't want to be calling any methods against `null`, even though it would work in theory (and if the compiler allows it currently, this is now a feature)? Best regards, Mahied Maruf From ethan at mccue.dev Fri Jan 23 15:29:22 2026 From: ethan at mccue.dev (Ethan McCue) Date: Fri, 23 Jan 2026 10:29:22 -0500 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: To add a concrete example, here is a Java collections library written by CERN https://github.com/kzn/colt They make instances of classes specifically to allow static methods to create aliases https://github.com/kzn/colt/blob/master/src%2Fhep%2Faida%2Fbin%2FBinFunctions1D.java#L12 And this is done liberally throughout the library, as well as code which uses the library (which is reasonable to assume given that it was an intended usage pattern) private static final cern.jet.math.Functions F = cern.jet.math.Functions.functions; On Fri, Jan 23, 2026, 10:12?AM Red IO wrote: > I understand your idea. But I think that's exactly what the warning is > for. It suggests people to not access static data through objects. Making > it an hard error would likely break many code bases requiring massive > migrations without any real benefits. > > Treating warnings as errors and annotating necessary warnings with > suppress warnings and a comment should be best practice anyway. > > Great regards > RedIODev > > On Fri, Jan 23, 2026, 13:42 Amazing Code > wrote: > >> I am writing to propose a language enhancement regarding the handling of >> static member access in Java. >> *Issue* >> >> Java currently permits static fields and methods to be accessed through >> object references, despite static members belonging strictly to the class. >> This behavior is often misleading and can create confusion, especially in >> large codebases or among less-experienced developers. >> >> Example: >> >> MyClass obj = new MyClass(); >> obj.staticMethod(); // Currently allowed, but confusing >> >> *Proposed Enhancement* >> >> I request consideration of a change that *disallows access to static >> members via object references*, enforcing access exclusively through the >> class name. This would convert the current warning into a *compile-time >> error*. >> *Rationale* >> >> - >> >> Prevents misconceptions about instance vs. class-level behavior >> - >> >> Improves code clarity and consistency >> - >> >> Reduces maintenance complexity in enterprise applications >> - >> >> Encourages best practices already recommended by the community >> >> *Suggested Requirements* >> >> 1. >> >> Compiler should produce an error when static members are accessed >> through object references. >> 2. >> >> Error messages should explicitly guide developers to use class-based >> access. >> 3. >> >> Rules should apply to static fields, static methods, and static >> nested types. >> 4. >> >> Optionally, provide a compiler flag for backward compatibility during >> migration. >> >> *Conclusion* >> >> Restricting object-based access to static members would strengthen >> language clarity and help eliminate a common source of misunderstanding. I >> kindly request your consideration of this enhancement for future Java >> releases. >> >> Thank you for your time and continued work on the Java platform. >> >> Sincerely, >> *Kamlesh Kohli* >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Fri Jan 23 15:30:00 2026 From: ethan at mccue.dev (Ethan McCue) Date: Fri, 23 Jan 2026 10:30:00 -0500 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: https://github.com/kzn/colt/blob/master/src%2Fcern%2Fcolt%2Fmatrix%2Flinalg%2FSeqBlas.java#L33 . Missed a link on that last one On Fri, Jan 23, 2026, 10:29?AM Ethan McCue wrote: > To add a concrete example, here is a Java collections library written by > CERN > > https://github.com/kzn/colt > > They make instances of classes specifically to allow static methods to > create aliases > > > https://github.com/kzn/colt/blob/master/src%2Fhep%2Faida%2Fbin%2FBinFunctions1D.java#L12 > > And this is done liberally throughout the library, as well as code which > uses the library (which is reasonable to assume given that it was an > intended usage pattern) > > private static final cern.jet.math.Functions F = > cern.jet.math.Functions.functions; > > > > > > On Fri, Jan 23, 2026, 10:12?AM Red IO wrote: > >> I understand your idea. But I think that's exactly what the warning is >> for. It suggests people to not access static data through objects. Making >> it an hard error would likely break many code bases requiring massive >> migrations without any real benefits. >> >> Treating warnings as errors and annotating necessary warnings with >> suppress warnings and a comment should be best practice anyway. >> >> Great regards >> RedIODev >> >> On Fri, Jan 23, 2026, 13:42 Amazing Code >> wrote: >> >>> I am writing to propose a language enhancement regarding the handling of >>> static member access in Java. >>> *Issue* >>> >>> Java currently permits static fields and methods to be accessed through >>> object references, despite static members belonging strictly to the class. >>> This behavior is often misleading and can create confusion, especially in >>> large codebases or among less-experienced developers. >>> >>> Example: >>> >>> MyClass obj = new MyClass(); >>> obj.staticMethod(); // Currently allowed, but confusing >>> >>> *Proposed Enhancement* >>> >>> I request consideration of a change that *disallows access to static >>> members via object references*, enforcing access exclusively through >>> the class name. This would convert the current warning into a *compile-time >>> error*. >>> *Rationale* >>> >>> - >>> >>> Prevents misconceptions about instance vs. class-level behavior >>> - >>> >>> Improves code clarity and consistency >>> - >>> >>> Reduces maintenance complexity in enterprise applications >>> - >>> >>> Encourages best practices already recommended by the community >>> >>> *Suggested Requirements* >>> >>> 1. >>> >>> Compiler should produce an error when static members are accessed >>> through object references. >>> 2. >>> >>> Error messages should explicitly guide developers to use class-based >>> access. >>> 3. >>> >>> Rules should apply to static fields, static methods, and static >>> nested types. >>> 4. >>> >>> Optionally, provide a compiler flag for backward compatibility >>> during migration. >>> >>> *Conclusion* >>> >>> Restricting object-based access to static members would strengthen >>> language clarity and help eliminate a common source of misunderstanding. I >>> kindly request your consideration of this enhancement for future Java >>> releases. >>> >>> Thank you for your time and continued work on the Java platform. >>> >>> Sincerely, >>> *Kamlesh Kohli* >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 23 15:34:51 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Jan 2026 10:34:51 -0500 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: <395d2c5f-59e7-4d14-8b5e-64f5fb1a6a5e@oracle.com> Indeed, this "feature" is a terrible feature.? In addition to being bug-bait, it has interfered with the design of several language features.? It is an example of how trying to be too helpful is actually unhelpful. However, the significant part of the request is not "here's a change that I think is beneficial, that can be evaluated on the merits"; it is "here is a language change that will break existing programs, that has to be evaluated on whether the incompatibility is justified."? While the bar for the former is very high, the bar for the latter is astronomically high. On 1/23/2026 7:56 AM, Tagir Valeev wrote: > Hello! > > By the way, this "feature" complicates the static analysis, so I'd > like if we get rid of it. There's even a bug in IntelliJ IDEA, which I > don't want to fix. Try the following class: > > class Test { > ? static void test() { > ? ? System.out.println("Static"); > ? } > > ? static void process(Test test) { > ? ? test.test(); > ? } > > ? public static void main(String[] args) { > ? ? process(null); > ? } > } > > There's a false-positive warning at `process(null)`:?Passing 'null' > argument to parameter annotated as @NotNull, despite the code runs > perfectly. IntelliJ IDEA assumes that if you dereference the `test` > variable via `test.test()` call, then `test` must not be null, so > passing `null` to the `process` method should be disallowed. At this > stage of analysis, we cannot spend time on resolving the `test()` call > and checking whether it's static or not, so we just assume that nobody > in their sane mind calls static methods on instance qualifiers. > > By the way, there's a warning already. It's just turned off by default. > > C:\Temp>javac.exe -Xlint:static Test.java > Test.java:7: warning: [static] static method should be qualified by > type name, Test, instead of by an expression > ? ? test.test(); > ? ? ? ? ^ > 1 warning > > With best regards, > Tagir Valeev. > > On Fri, Jan 23, 2026 at 1:43?PM Amazing Code > wrote: > > I am writing to propose a language enhancement regarding the > handling of static member access in Java. > > > *Issue* > > Java currently permits static fields and methods to be accessed > through object references, despite static members belonging > strictly to the class. This behavior is often misleading and can > create confusion, especially in large codebases or among > less-experienced developers. > > Example: > > |MyClass obj = new MyClass(); obj.staticMethod(); // Currently > allowed, but confusing | > > > *Proposed Enhancement* > > I request consideration of a change that *disallows access to > static members via object references*, enforcing access > exclusively through the class name. This would convert the current > warning into a *compile-time error*. > > > *Rationale* > > * > > Prevents misconceptions about instance vs. class-level behavior > > * > > Improves code clarity and consistency > > * > > Reduces maintenance complexity in enterprise applications > > * > > Encourages best practices already recommended by the community > > > *Suggested Requirements* > > 1. > > Compiler should produce an error when static members are > accessed through object references. > > 2. > > Error messages should explicitly guide developers to use > class-based access. > > 3. > > Rules should apply to static fields, static methods, and > static nested types. > > 4. > > Optionally, provide a compiler flag for backward compatibility > during migration. > > > *Conclusion* > > Restricting object-based access to static members would strengthen > language clarity and help eliminate a common source of > misunderstanding. I kindly request your consideration of this > enhancement for future Java releases. > > Thank you for your time and continued work on the Java platform. > > Sincerely, > *Kamlesh Kohli* > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jan 23 15:35:24 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 23 Jan 2026 09:35:24 -0600 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: My $0.02: This is an easy call. The answer is that it's not worth changing because (b) this would cause legacy to to start failing to compile, which is violates Java's stellar reputation for backward compatibility, and (b) there is already a perfectly reasonable workaround, i.e. -Xlint:static -Werror. I can't think of very many examples in the past. where Java made such a change (that is, where (a) and (b) were true. I guess the treatment of "_" and "yield" count. -Archie On Fri, Jan 23, 2026 at 6:43?AM Amazing Code wrote: > I am writing to propose a language enhancement regarding the handling of > static member access in Java. > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From contact at mechite.com Fri Jan 23 17:34:38 2026 From: contact at mechite.com (Mahied Maruf) Date: Fri, 23 Jan 2026 17:34:38 +0000 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: > Archie Cobbs says: > This is an easy call. The answer is that it's not worth changing > because this would cause legacy to to start failing > ... > and there is already a perfectly reasonable workaround. I agree. I do think it's worth issuing warnings _by default_, and turning this into _opt-out_ rather than _opt-in_. > I can't think of very many examples in the past. where Java made such > a change (that is, where (a) and (b) were true. > > I guess the treatment of "_" and "yield" count. Just to say that `yield` also does not count. It is a contextual keyword AFAIK. There have been an extremely minimal amount of breaking language changes, as far as I am aware, basically zero of Amber. Best regards, Mahied Maruf From brian.goetz at oracle.com Fri Jan 23 17:40:25 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Jan 2026 12:40:25 -0500 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: <92028a48-2264-46ed-9121-f20873b04568@oracle.com> >> This is an easy call. The answer is that it's not worth changing >> because this would cause legacy to to start failing >> ... >> and there is already a perfectly reasonable workaround. > I agree. I do think it's worth issuing warnings _by default_, and > turning this into _opt-out_ rather than _opt-in_. It's easy to have this opinion for any given warning; for every warning, there is _someone_ who thinks it should be turned up (if not all the way to error.)? ?But these decisions need to be made within a more coherent strategy than "this bug pattern really bugs me".? Otherwise, we set `javac` on a path where its secondary mission is to be a mediocre static analysis engine.? Our preference is to leave that to tools whose _primary_ mission is static analysis -- of which there are many, and they are all going to be much better at it than `javac`, because `javac` has a different primary mission. From contact at mechite.com Fri Jan 23 18:15:14 2026 From: contact at mechite.com (Mahied Maruf) Date: Fri, 23 Jan 2026 18:15:14 +0000 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: <92028a48-2264-46ed-9121-f20873b04568@oracle.com> References: <92028a48-2264-46ed-9121-f20873b04568@oracle.com> Message-ID: >> Mahied Maruf says: >> I agree. I do think it's worth issuing warnings _by default_, and >> turning this into _opt-out_ rather than _opt-in_. > Brian Goetz says: > It's easy to have this opinion for any given warning; for every warning, > there is _someone_ who thinks it should be turned up (if not all the way > to error.)? ?But these decisions need to be made within a more coherent > strategy than "this bug pattern really bugs me".? Otherwise, we set > `javac` on a path where its secondary mission is to be a mediocre static > analysis engine.? Our preference is to leave that to tools whose > _primary_ mission is static analysis -- of which there are many, and > they are all going to be much better at it than `javac`, because `javac` > has a different primary mission. This makes sense. I don't doubt either that this has probably already been discussed. Just to say that the specific language feature being dicussed here is minor enough and well-documented (in my opinion) such the bar needs to be that it genuinely interferes enough with a new language feature (or something else motivates it) to be changed. I'm on the side here of no change. The language generally has a lot more 'donts' than 'dos' for writing what we would call 'idomatic' software. The case of the IDE not being able to properly analyze: class Test ... Test test = null; test.test(); ...as not being disallowed, can be attributed to an effort not being made in being exhaustive with implementing this static analysis. > Tagir Valeev says: > At this stage of analysis, we cannot spend time on resolving the > `test()` call and checking whether it's static or not... I assume by "spend time" we are implying that it's an expensive check to make, and I would say that (a) this can be rare enough to forgo the check, and (b) the language being expensive to analyze is not (should not be said as being) at flaw of the language. The static analyzer is generally supposed to be "in good faith" estimation of what will happen at runtime. Best regards, Mahied Maruf From attila.kelemen85 at gmail.com Sat Jan 24 13:08:33 2026 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 24 Jan 2026 14:08:33 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: > > My $0.02: This is an easy call. The answer is that it's not worth changing > because (b) this would cause legacy to to start failing to compile, which > is violates Java's stellar reputation for backward compatibility, and (b) > there is already a perfectly reasonable workaround, i.e. -Xlint:static > -Werror. > I'm not arguing that the original request should be implemented and break existing code (bad as they are). However, this suggestion doesn't really work, because javac doesn't support different sets of values for `Werror` and for mere warnings. That is, I usually want to turn on almost everything for `Xlint` , but I definitely don't want every warning to be an error (most notably, I don't want `@deprecated` to immediately fail compilation, but I want it to be reported as a warning). -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Jan 24 13:18:26 2026 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 24 Jan 2026 14:18:26 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Hi! On Sat, Jan 24, 2026, 14:09 Attila Kelemen wrote: > My $0.02: This is an easy call. The answer is that it's not worth changing >> because (b) this would cause legacy to to start failing to compile, which >> is violates Java's stellar reputation for backward compatibility, and (b) >> there is already a perfectly reasonable workaround, i.e. -Xlint:static >> -Werror. >> > > I'm not arguing that the original request should be implemented and break > existing code (bad as they are). However, this suggestion doesn't really > work, because javac doesn't support different sets of values for `Werror` > and for mere warnings. That is, I usually want to turn on almost everything > for `Xlint` , but I definitely don't want every warning to be an error > (most notably, I don't want `@deprecated` to immediately fail compilation, > but I want it to be reported as a warning). > Exactly my thoughts. With best regards, Tagir Valeev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sat Jan 24 14:07:47 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sat, 24 Jan 2026 08:07:47 -0600 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Funny you should mention that... :) In JDK 26+ you will be able to do this via flags like -Werror:static See https://bugs.openjdk.org/browse/JDK-8349847 for details. -Archie On Sat, Jan 24, 2026 at 7:08?AM Attila Kelemen wrote: > My $0.02: This is an easy call. The answer is that it's not worth changing >> because (b) this would cause legacy to to start failing to compile, which >> is violates Java's stellar reputation for backward compatibility, and (b) >> there is already a perfectly reasonable workaround, i.e. -Xlint:static >> -Werror. >> > > I'm not arguing that the original request should be implemented and break > existing code (bad as they are). However, this suggestion doesn't really > work, because javac doesn't support different sets of values for `Werror` > and for mere warnings. That is, I usually want to turn on almost everything > for `Xlint` , but I definitely don't want every warning to be an error > (most notably, I don't want `@deprecated` to immediately fail compilation, > but I want it to be reported as a warning). > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Jan 24 14:46:36 2026 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 24 Jan 2026 15:46:36 +0100 (CET) Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: <1335413324.24568864.1769265996982.JavaMail.zimbra@univ-eiffel.fr> > From: "Archie Cobbs" > To: "attila kelemen85" > Cc: "Amazing Code" , "amber-dev" > > Sent: Saturday, January 24, 2026 3:07:47 PM > Subject: Re: Incident Report 9079511: Java Language Enhancement: Disallow access > to static members via object references > Funny you should mention that... :) > In JDK 26+ you will be able to do this via flags like -Werror:static > See [ https://bugs.openjdk.org/browse/JDK-8349847 | > https://bugs.openjdk.org/browse/JDK-8349847 ] for details. > -Archie IDEs were able to do that since a long time, it was long overdue. Soon we will be able to turn those nullcheck warnings into errors. Thanks Archie. R?mi > On Sat, Jan 24, 2026 at 7:08 AM Attila Kelemen < [ > mailto:attila.kelemen85 at gmail.com | attila.kelemen85 at gmail.com ] > wrote: >>> My $0.02: This is an easy call. The answer is that it's not worth changing >>> because (b) this would cause legacy to to start failing to compile, which is >>> violates Java's stellar reputation for backward compatibility, and (b) there is >>> already a perfectly reasonable workaround, i.e. -Xlint:static -Werror . >> I'm not arguing that the original request should be implemented and break >> existing code (bad as they are). However, this suggestion doesn't really work, >> because javac doesn't support different sets of values for `Werror` and for >> mere warnings. That is, I usually want to turn on almost everything for `Xlint` >> , but I definitely don't want every warning to be an error (most notably, I >> don't want `@deprecated` to immediately fail compilation, but I want it to be >> reported as a warning). > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jan 24 14:47:00 2026 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 24 Jan 2026 15:47:00 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Oh, wow. I did vaguely recall mentioning this missing feature. Apparently, it was on a thread you started: https://mail.openjdk.org/pipermail/compiler-dev/2023-October/024417.html Well, thank you, I guess. I should maybe contribute instead of just complaining, and things might happen earlier :). I'm just not so great with long bureaucratic processes, and generally lost how to start if I believe a feature (even if small) should be implemented in the JDK (or related tools). That is, my general expectation would be that if I just send a PR, then it would be ignored and lost within the many things of the JDK (at least as far as I have seen, actually implementing it is usually secondary to having it discussed with relevant people). Archie Cobbs ezt ?rta (id?pont: 2026. jan. 24., Szo, 15:08): > Funny you should mention that... :) > > In JDK 26+ you will be able to do this via flags like -Werror:static > > See https://bugs.openjdk.org/browse/JDK-8349847 for details. > > -Archie > > On Sat, Jan 24, 2026 at 7:08?AM Attila Kelemen > wrote: > >> My $0.02: This is an easy call. The answer is that it's not worth >>> changing because (b) this would cause legacy to to start failing to >>> compile, which is violates Java's stellar reputation for backward >>> compatibility, and (b) there is already a perfectly reasonable workaround, >>> i.e. -Xlint:static -Werror. >>> >> >> I'm not arguing that the original request should be implemented and break >> existing code (bad as they are). However, this suggestion doesn't really >> work, because javac doesn't support different sets of values for `Werror` >> and for mere warnings. That is, I usually want to turn on almost everything >> for `Xlint` , but I definitely don't want every warning to be an error >> (most notably, I don't want `@deprecated` to immediately fail compilation, >> but I want it to be reported as a warning). >> > > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sat Jan 24 15:52:21 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sat, 24 Jan 2026 09:52:21 -0600 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: There are two ways to look at OpenJDK with its zoo of components, sub-groups, CSR's, processes, hierarchies, etc: (a) It's the most bureaucratic open source project in the world (b) it's the most incredible, productive, and reliable open source project in the world*. I think both are true. Moveover I don't think it would be possible to have (b) without (a). So yes, it's unfortunate but unavoidable that it's rarely as easy as filing a PR, which is what most people familiar with "open source" people are used to (including me until I joined this project). That means more effort is required, which means that often one just doesn't have the time to deal with it. Nothing wrong with that. -Archie * One could argue that Linux shows the "benevolent overlord" model works too, but remember "Linux" only refers to the kernel; everything else is spread across many other organizations, whereas OpenJDK is a one-stop shop for the JVM, the Java language, and all the various JDK modules. On Sat, Jan 24, 2026 at 8:47?AM Attila Kelemen wrote: > Well, thank you, I guess. I should maybe contribute instead of just > complaining, and things might happen earlier :). I'm just not so great with > long bureaucratic processes, and generally lost how to start if I believe a > feature (even if small) should be implemented in the JDK (or related > tools). That is, my general expectation would be that if I just send a PR, > then it would be ignored and lost within the many things of the JDK (at > least as far as I have seen, actually implementing it is usually secondary > to having it discussed with relevant people). > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Jan 24 19:16:27 2026 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 24 Jan 2026 20:16:27 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Cool! On Sat, Jan 24, 2026, 19:38 Archie Cobbs wrote: > Funny you should mention that... :) > > In JDK 26+ you will be able to do this via flags like -Werror:static > > See https://bugs.openjdk.org/browse/JDK-8349847 for details. > > -Archie > > On Sat, Jan 24, 2026 at 7:08?AM Attila Kelemen > wrote: > >> My $0.02: This is an easy call. The answer is that it's not worth >>> changing because (b) this would cause legacy to to start failing to >>> compile, which is violates Java's stellar reputation for backward >>> compatibility, and (b) there is already a perfectly reasonable workaround, >>> i.e. -Xlint:static -Werror. >>> >> >> I'm not arguing that the original request should be implemented and break >> existing code (bad as they are). However, this suggestion doesn't really >> work, because javac doesn't support different sets of values for `Werror` >> and for mere warnings. That is, I usually want to turn on almost everything >> for `Xlint` , but I definitely don't want every warning to be an error >> (most notably, I don't want `@deprecated` to immediately fail compilation, >> but I want it to be reported as a warning). >> > > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Sun Jan 25 05:33:52 2026 From: chen.l.liang at oracle.com (Chen Liang) Date: Sun, 25 Jan 2026 05:33:52 +0000 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Referring to the "bureaucratic process": Note amber is related to language specification, which is extremely sensitive to addition due to compatibility and almost no ability to rollback a wrong decision once it has been made. (Consider a counterexample of ?:'s null-long merge to Long, and Long-long merge back to long, causing NPE) > how to start if I believe a feature (even if small) should be implemented in the JDK (or related tools). The JDK is not eager to add features too - it means the JDK has to support it decades later, and a feature must interact with the rest of the features, which can exponentially increase the complexity. General changes in the JDK usually address specific problems without introducing the aforementioned risk or complexity. That's why you see JDK guide usually recommend starting with test coverage addition or simple bug fixes. And that is also most of the development work in the JDK, even though they don't appear attractive. I think if you want a feature, it's better for you to start from the use case - what problem is being addressed by that feature? Are there other solutions, including poor ones or strawman that you can reject to support your proposal? What will happen a decade later to this feature? You can try reading through the JEP documents; they usually provide convincing answers to my questions listed ahead. Regards, Chen Liang ________________________________ From: amber-dev on behalf of Attila Kelemen Sent: Saturday, January 24, 2026 8:47 AM To: Archie Cobbs Cc: Amazing Code ; amber-dev at openjdk.org Subject: Re: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references Oh, wow. I did vaguely recall mentioning this missing feature. Apparently, it was on a thread you started: https://mail.openjdk.org/pipermail/compiler-dev/2023-October/024417.html Well, thank you, I guess. I should maybe contribute instead of just complaining, and things might happen earlier :). I'm just not so great with long bureaucratic processes, and generally lost how to start if I believe a feature (even if small) should be implemented in the JDK (or related tools). That is, my general expectation would be that if I just send a PR, then it would be ignored and lost within the many things of the JDK (at least as far as I have seen, actually implementing it is usually secondary to having it discussed with relevant people). Archie Cobbs > ezt ?rta (id?pont: 2026. jan. 24., Szo, 15:08): Funny you should mention that... :) In JDK 26+ you will be able to do this via flags like -Werror:static See https://bugs.openjdk.org/browse/JDK-8349847 for details. -Archie On Sat, Jan 24, 2026 at 7:08?AM Attila Kelemen > wrote: My $0.02: This is an easy call. The answer is that it's not worth changing because (b) this would cause legacy to to start failing to compile, which is violates Java's stellar reputation for backward compatibility, and (b) there is already a perfectly reasonable workaround, i.e. -Xlint:static -Werror. I'm not arguing that the original request should be implemented and break existing code (bad as they are). However, this suggestion doesn't really work, because javac doesn't support different sets of values for `Werror` and for mere warnings. That is, I usually want to turn on almost everything for `Xlint` , but I definitely don't want every warning to be an error (most notably, I don't want `@deprecated` to immediately fail compilation, but I want it to be reported as a warning). -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From a.gegg at btinternet.com Sun Jan 25 18:37:14 2026 From: a.gegg at btinternet.com (Andy Gegg) Date: Sun, 25 Jan 2026 18:37:14 +0000 Subject: JEP 468 updating non-updatable fields Message-ID: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Hello, I apologise for coming late to the party here - Records have been of limited use to me but Mr Goetz's email on carrier classes is something that would be very useful so I've been thinking about the consequences. Since? carrier classes and records are for data, in a database application somewhere or other you're going to get database ids in records: record MyRec(int dbId, String name,...) While everything is immutable this is fine but JEP 468 opens up the possibility of mutation: MyRec rec?= readDatabase(...); rec = rec with {name="...";}; writeDatabase(rec); which is absolutely fine and what an application wants to do. But: MyRec rec = readDatabase(...); rec = rec with {dbId++;}; writeDatabase(rec); is disastrous.? There's no way the canonical constructor invoked from 'with' can detect stupidity nor can whatever the database access layer does. In the old days, the lack of a 'setter' would usually prevent stupid code - the above could be achieved, obviously, but the code is devious enough to make people stop and think (one hopes). Here there is nothing to say "do not update this!!!" except code comments, JavaDoc and naming conventions. It's not always obvious which fields may or may not be changed in the application. record MyRec(int dbId, int fatherId,...) probably doesn't want rec = rec with { fatherId = ... } but a HR application will need to be able to do: record MyRec(int dbId, int departmentId, ...); ... rec = rec with { departmentId = newDept; }; Clearly, people can always write stupid code (guilty...) and the current state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, ...);) which is enough to stop people using records here but carrier classes will be very tempting and that brings derived creation back to the fore. It's not just database ids which might need restricting from update, e.g. timestamps (which are better done in the database layer) and no doubt different applications will have their own business case restrictions. Thank you for your time, Andy Gegg -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Jan 25 19:09:04 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 25 Jan 2026 14:09:04 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Message-ID: The important mental model here is that a reconstruction (`with`) expression is "just" a syntactic optimization for: ?- destructure with the canonical deconstruction pattern ?- mutate the components ?- reconstruct with the primary constructor So the root problem here is not the reconstruction expression; if you can bork up your application state with a reconstruction expression, you can bork it up without one. Primary constructors can enforce invariants _on_ or _between_ components, such as: ? ? record Rational(int num, int denom) { ? ? ? ? Rational { if (denom == 0) throw ... } ? ? } or ? ? record Range(int lo, int hi) { ? ? ? ? Range { if (lo > hi) throw... } ? ? } What they can't do is express invariants between the record / carrier state and "the rest of the system", because they are supposed to be simple data carriers, not serialized references to some external system. A class that models a database row in this way is complecting entity state with an external entity id.? By modeling in this way, you have explicitly declared that ? ? rec with { dbId++ } *is explicitly OK* in your system; that the components of the record can be freely combined in any way (modulo enforced cross-component invariants).? And there are systems in which this is fine!? But you're imagining (correctly) that this modeling technique will be used in systems in which this is not fine. The main challenge here is that developers will be so attracted to the syntactic concision that they will willfully ignore the semantic inconsistencies they are creating. On 1/25/2026 1:37 PM, Andy Gegg wrote: > Hello, > I apologise for coming late to the party here - Records have been of > limited use to me but Mr Goetz's email on carrier classes is something > that would be very useful so I've been thinking about the consequences. > > Since? carrier classes and records are for data, in a database > application somewhere or other you're going to get database ids in > records: > record MyRec(int dbId, String name,...) > > While everything is immutable this is fine but JEP 468 opens up the > possibility of mutation: > > MyRec rec?= readDatabase(...); > rec = rec with {name="...";}; > writeDatabase(rec); > > which is absolutely fine and what an application wants to do. But: > MyRec rec = readDatabase(...); > rec = rec with {dbId++;}; > writeDatabase(rec); > > is disastrous.? There's no way the canonical constructor invoked from > 'with' can detect stupidity nor can whatever the database access layer > does. > > In the old days, the lack of a 'setter' would usually prevent stupid > code - the above could be achieved, obviously, but the code is devious > enough to make people stop and think (one hopes). > > Here there is nothing to say "do not update this!!!" except code > comments, JavaDoc and naming conventions. > > It's not always obvious which fields may or may not be changed in the > application. > > record MyRec(int dbId, int fatherId,...) > probably doesn't want > rec = rec with { fatherId = ... } > > but a HR application will need to be able to do: > > record MyRec(int dbId, int departmentId, ...); > ... > rec = rec with { departmentId = newDept; }; > > Clearly, people can always write stupid code (guilty...) and the > current state of play obviously allows the possibility (rec = new > MyRec(rec.dbId++, ...);) which is enough to stop people using records > here but carrier classes will be very tempting and that brings derived > creation back to the fore. > > It's not just database ids which might need restricting from update, > e.g. timestamps (which are better done in the database layer) and no > doubt different applications will have their own business case > restrictions. > > Thank you for your time, > Andy Gegg > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amazingcodewithus at gmail.com Mon Jan 26 03:37:53 2026 From: amazingcodewithus at gmail.com (Amazing Code) Date: Mon, 26 Jan 2026 09:07:53 +0530 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Hello all, Thanks again for the thoughtful discussion so far. Based on the thread I?d like to suggest a practical, non-breaking way forward that gives teams the *option *to enforce instance-qualified static access as a hard rule, while preserving backward compatibility for everyone else. *Proposal (summary)* Provide an opt-in enforcement stack consisting of: 1. A reference *javac plugin* (e.g. StaticAccessEnforcer) that fails compilation on instance-qualified static accesses when enabled; 2. companion *Maven/Gradle plugins* that automatically enable the plugin for projects that add the dependency or set a property; 3. *IDE quick-fixers* and a command-line *codemod* to assist automated migration; and 4. when/if available, recommend using per-category Werror (e.g. `-Werror:static`) for CI once OpenJDK supports it broadly. *Why this approach?* - *No language break* ? default javac behavior remains unchanged. - *Opt-in* ? new projects or teams can opt to treat the warning as an error by adding a dependency/flag. - *Ecosystem friendly* ? leverages javac plugin SPI, Checker Framework/ErrorProne patterns, and IDE support. :contentReference[oaicite:11]{index=11} *Implementation sketch* - Build reference plugin using the `Plugin` SPI and publish as a Maven artifact. Example invocation: `javac -Xplugin:StaticAccessEnforcer ...` or via maven-compiler-plugin/Gradle compiler args. :contentReference[oaicite:12]{index=12} - Provide a `static-access-enforcer` Gradle/Maven plugin that wires the `-Xplugin` or annotation processor automatically. - Include automated codemod tooling (JavaParser/Eclipse JDT) and an IntelliJ quick-fix to perform safe replacements. - Plugin modes: `report` / `warn` / `error`, and support `@SuppressWarnings("static")` or per-file exclusion. *Request* If this sounds reasonable, I can prepare: - a small reference implementation (javac plugin + sample Maven config), and - a short JEP-style note describing migration story and sample fixes. Thank you for considering this compromise ? it preserves Java?s compatibility guarantees while giving teams a practical path to stronger, enforced conventions. Kind regards, Kamlesh On Sun, 25 Jan 2026 at 11:04, Chen Liang wrote: > Referring to the "bureaucratic process": Note amber is related to language > specification, which is extremely sensitive to addition due to > compatibility and almost no ability to rollback a wrong decision once it > has been made. (Consider a counterexample of ?:'s null-long merge to Long, > and Long-long merge back to long, causing NPE) > > > how to start if I believe a feature (even if small) should be > implemented in the JDK (or related tools). > > The JDK is not eager to add features too - it means the JDK has to support > it decades later, and a feature must interact with the rest of the > features, which can exponentially increase the complexity. > > General changes in the JDK usually address specific problems without > introducing the aforementioned risk or complexity. That's why you see JDK > guide usually recommend starting with test coverage addition or simple bug > fixes. And that is also most of the development work in the JDK, even > though they don't appear attractive. > > I think if you want a feature, it's better for you to start from the use > case - what problem is being addressed by that feature? Are there other > solutions, including poor ones or strawman that you can reject to support > your proposal? What will happen a decade later to this feature? You can try > reading through the JEP documents; they usually provide convincing answers > to my questions listed ahead. > > Regards, > Chen Liang > > ------------------------------ > *From:* amber-dev on behalf of Attila > Kelemen > *Sent:* Saturday, January 24, 2026 8:47 AM > *To:* Archie Cobbs > *Cc:* Amazing Code ; amber-dev at openjdk.org < > amber-dev at openjdk.org> > *Subject:* Re: Incident Report 9079511: Java Language Enhancement: > Disallow access to static members via object references > > Oh, wow. I did vaguely recall mentioning this missing feature. Apparently, > it was on a thread you started: > https://mail.openjdk.org/pipermail/compiler-dev/2023-October/024417.html > > Well, thank you, I guess. I should maybe contribute instead of just > complaining, and things might happen earlier :). I'm just not so great with > long bureaucratic processes, and generally lost how to start if I believe a > feature (even if small) should be implemented in the JDK (or related > tools). That is, my general expectation would be that if I just send a PR, > then it would be ignored and lost within the many things of the JDK (at > least as far as I have seen, actually implementing it is usually secondary > to having it discussed with relevant people). > > Archie Cobbs ezt ?rta (id?pont: 2026. jan. 24., > Szo, 15:08): > > Funny you should mention that... :) > > In JDK 26+ you will be able to do this via flags like -Werror:static > > See https://bugs.openjdk.org/browse/JDK-8349847 for details. > > -Archie > > On Sat, Jan 24, 2026 at 7:08?AM Attila Kelemen > wrote: > > My $0.02: This is an easy call. The answer is that it's not worth changing > because (b) this would cause legacy to to start failing to compile, which > is violates Java's stellar reputation for backward compatibility, and (b) > there is already a perfectly reasonable workaround, i.e. -Xlint:static > -Werror. > > > I'm not arguing that the original request should be implemented and break > existing code (bad as they are). However, this suggestion doesn't really > work, because javac doesn't support different sets of values for `Werror` > and for mere warnings. That is, I usually want to turn on almost everything > for `Xlint` , but I definitely don't want every warning to be an error > (most notably, I don't want `@deprecated` to immediately fail compilation, > but I want it to be reported as a warning). > > > > -- > Archie L. Cobbs > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From contact at mechite.com Mon Jan 26 04:09:28 2026 From: contact at mechite.com (Mahied Maruf) Date: Mon, 26 Jan 2026 04:09:28 +0000 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: > Kamlesh says: > If this sounds reasonable, I can prepare: > - small reference implementation (javac plugin + sample Maven config) > - short JEP-style note describing migration story and sample fixes. Just to say that I personally do not find this enough of a problem to demand individual SPI providers (and/or Maven, Gradle, IDE plugins). The existing tooling does a fantastic job, and also just to note: > IntelliJ quick-fix to perform safe replacements ...IDEA can already do this reasonably well. "Qualify static 'example()' call with reference to class Example". The only thing that was mentioned on IDEA was the inability to _not_ trigger nullability inspections against a `null` reference when calling static methods against it. That would actually be _weakening_ the static analysis, but making it semantically correct. I consider this a solution looking for a problem. Best regards, Mahied Maruf From develop4lasu at gmail.com Mon Jan 26 06:31:31 2026 From: develop4lasu at gmail.com (=?UTF-8?Q?Marek_Kozie=C5=82?=) Date: Mon, 26 Jan 2026 07:31:31 +0100 Subject: Explicit end-of-use: the 'forget' keyword Message-ID: Hi, Java lacks a way to express intentional end-of-use. This leads to: - accidental reuse of values that are no longer semantically valid - unclear intent during code review - refactoring hazards when scopes expand I?m interested in revisiting and refining this concept (raw form was discussed during Project Coin in 2009). The goal is to better understand the problem space so that, if and when the benefits clearly justify it, a well-formed direction could be considered. OVERVIEW FEATURE SUMMARY: The forget keyword prevents further access to a variable, parameter, or field within a defined scope, attempts to access the variable in forbidden scope will result in a compile-time error. MAJOR ADVANTAGE: This change makes variable and resource lifetimes explicit and compiler-enforced, improving code clarity and predictability. MAJOR BENEFITS: Allows explicitly removing a variable from the active context (in terms of accessibility), which is currently: impossible for final variables (only comments can be used), impossible for method parameters (except assigning null to non-final references), impossible for fields, cumbersome for local variables, requiring artificial blocks (extra lines and indentation). Makes it possible to explicitly declare that a variable should no longer be used or no longer represents valid data in the current scope, including cases where continued use would be undesirable or error-prone (like when there is method created for this purpose). Preserves code quality over time, avoiding degradation caused by = null assignments, comments-only conventions, or artificial scoping blocks. MAJOR DISADVANTAGE: The introduction of a new reserved keyword introduces potential source incompatibilities with existing codebases that define identifiers named forget. ALTERNATIVES: Java currently provides only scope-based lifetime control (blocks and try-with-resources). It lacks a general, explicit, and compiler-enforced mechanism to terminate variable usability at an arbitrary point within an existing scope. EXAMPLES Simple and Advanced Examples: forget var; // Variable is forgotten for the remainder of the current block or method (default behavior) forget var : scope; // Variable is forgotten inside the entire scope statement where scope could potentially be : if, for, while, try, label, static, method forget (var1, var2, ...); // Specified variables are forgotten for the remainder of the current block forget this.field; // Specified field are forgotten for the remainder of the current block forget (var1, var2, ...) { /* code */ }; // Specified variables are forgotten only inside the enclosed block void handleRequest(String request, String token) { if (!isTokenValid(token)) { throw new SecurityException("Invalid token"); } authorize(request, token); forget token /* used & contains sensitive info */; process(request); logger.debug("token was: " + token); // Compile-time error: 'token' has been forgotten and cannot be used } public Product(String name) { // constructor this.name = name.trim().intern(); forget name; // From now on, only use 'this.name'! // other constructor commands... if (isDuplicate(this.name)) { ... } // Always canonical, never raw input if (isDuplicate(name)) { ... } // Compile-time ERROR! } // * Forces usage of the correctly prepared value (this.name) only. // * Prevents code drift, maintenance bugs, or copy-paste errors that reference the raw parameter. // * Makes the constructor safer: no risk of mismatches or inconsistent logic. // * Reads as a contract: "from here on, don't touch the original argument!" Next Version Examples: forget ClassName.field; forget variable.field; forget (!variable); // Limit allowed variables to ones that are directly specified DETAILS SPECIFICATION: forget [ Identifier | ( IdentifierList ) ] [ : Scope | { block }]; IdentifierList: Identifier {, Identifier} Identifier: [ VariableIdentifier | this.FieldIdentifier ] The forget statement forbids any further use of the specified identifier in all subsequent expressions and statements within the declared scope in which the identifier would normally be accessible. COMPILATION: The variable is not physically erased (except it may be if not a field); rather, it is protected from any further access after the forget statement. Additionally, retaining the variable in the scope (but inaccessible) prevents situations where a developer tries to create a new variable with the same name after removing the forget statement, thereby enforcing consistent usage and avoiding hidden bugs. TESTING: Testing the forget statement is equivalent to testing variable scope after exiting a block?the variable becomes inaccessible. For fields, forget enforces access control, ensuring the field cannot be used within the specified scope for the remainder of its block or method.LIBRARY COMPATIBILITY The introduction of a new keyword (forget) may cause conflicts in codebases where forget is already used as an identifier. There are no other compatibility impacts. REFERENCES Current draft ? https://lasu2string.blogspot.com/2026/01/Java-forget-keyword.html Reddit discussion ? https://www.reddit.com/r/java/comments/1qhhf9y/proposal_introducing_the_forget_keyword_in_java/ Draft from 2009 ? https://lasu2string.blogspot.com/2009/03/forget-keyword-proposal_27.html Project coin 2009 discussion entry ? https://mail.openjdk.org/pipermail/coin-dev/2009-March/001093.html JDK-6189163 ? https://bugs.openjdk.org/browse/JDK-6189163 PROBLEMS Backward Compatibility: Introducing forget as a new reserved keyword will cause compilation errors in existing code that already uses forget as an identifier (variable, method, class, etc). Tooling Lag: IDEs, static analysis tools, and debuggers must all be updated to handle the new keyword and its effects on variable visibility. Code Readability: Misuse or overuse of forget could make code harder to maintain or follow if not used judiciously, especially if variables are forgotten in non-obvious places. Teaching and Onboarding: This feature introduces a new concept that must be documented and taught to all developers, which can increase the learning curve for Java. Migration Complexity: Automatic migration of legacy code may be challenging, particularly for projects that rely on forget as an existing identifier or which have established conventions for variable lifetime. Interaction with Scoping and Shadowing: The detailed behavior when variables are forgotten, shadowed, or reintroduced in inner scopes may lead to confusion and subtle bugs if not carefully specified and implemented. Reflection and Debugging: While reflective APIs themselves are not impacted, developers may be surprised by the presence of variables at runtime (for debugging or reflection) that are "forgotten" in the source code. Consistency Across Language Features: Defining consistent behavior for forget in new contexts (e.g., lambdas, anonymous classes, record classes) may require extra specification effort. Edge Cases and Specification Complexity: Fully specifying the semantics of forget for all cases?including fields, parameters, captured variables in inner/nested classes, and interaction with try/catch/finally?may be complex. Unused Feature Risk: There is a risk that the forget keyword will see little real-world use, or will be misunderstood, if not supported and encouraged by frameworks or coding standards. THE RIGHT AND THE WRONG WAY During the discussion, it became clear that the main conception of usage was not approached from the right angle. For example, the request JDK-6189163: New language feature: undeclare operator made a similar suggestion. Unfortunately, much of the focus was on "destroying" variables as a matter of code complexity and maintenance. This focus is problematic for several reasons: The compiler can already release variables when they are no longer needed. Focusing on disposing bytes of data is really a bad step as it shouldn't be a concern in the first place. Focusing on variable usability in this way actually decreases maintainability, since both current and future modifications would require additional checks to unlock variables or identify where a variable has been "undeclared" in the middle of code. This complicates code changes unnecessarily. At the same time, we should highlight that many language features are, to some extent, syntactic sugar from a usability perspective: Block-based local variable scoping - Enhanced for Loop - Final guard - Generics - Lambdas - Records - Switch Expressions - Text Blocks - Try-With-Resources - ... and many more Each of these features can absolutely be used in the wrong way, which may decrease code quality. On the other hand, when used properly, they can significantly reduce cognitive load and the time required for analysis, refactoring, and modification - and this path the change will aim to follow. So we should focus on situations where forget is actually beneficial in the short or long term: When code is actively being developed -especially complex one - there are situations where removing unnecessary variables from the context is beneficial. It's entirely possible that in the final version these variables will be placed into proper scopes with no need of forget, but until then forget would serve an important purpose during development. When modifying long or complex code?especially if an issue arises from the incorrect reuse of a variable: We could safeguard the variable with a comment explaining its intended use and the change made. This targeted fix is preferable to an unannotated quick fix (which leaves confusion for future maintainers) or an extensive refactor, which could disrupt code history and introduce new errors. Writing security-sensitive code where resources need to be released in a specific order that cannot be enforced using classic block structures. Explicitly excluding class variables from the current method scope when ussage could be harmfull(for example in efficiency aspect), helping to prevent accidental usage and improve code clarity. SIMPLIFIED VERSION Problem: Handling variable "forgetting" via scope control can introduce unnecessary complexity. To address this, an alternative version without scope customization could be considered. Simplified Solution: Instead of customizing scopes, forget could apply to the method/block scope. Caveat: It does NOT work for mutually exclusive scopes like parallel branches (if-else, switch-case). In those, you must restate forget in each branch. void handleRequest(String request, String token) { if (!isTokenValid(token)) { throw new SecurityException("Invalid token"); } if (forceLogOut()){ logOut(request, token); forget token; // prevent usage in the rest of this block and after the if-else ... } else { authorize(request, token); forget token; // 'forget' needs to be restated here ... } logger.debug("token was: " + token); // Compile-time error! } This approach is more rigid and resistant to refactoring mistakes if block scopes change. If more flexibility is needed, a more complex form like forget var : label; (for better control) can be introduced as an advanced feature. SUMMARY The forget keyword represents a natural evolution of Java's commitment to clear, explicit, and compiler-enforced language rules. By allowing developers to mark variables, parameters, or fields as no longer usable within a defined scope, forget makes variable lifetimes and resource management visible and deliberate. This approach eliminates ambiguity in code, prevents accidental misuse, and reinforces Java?s tradition of making correctness and safety a language guarantee, not just a convention. Like Java?s robust type system and scoping mechanisms, forget enhances code clarity, maintainability, and reliability. -- Greetings Marek Kozie? ( Lasu ) -------------- next part -------------- An HTML attachment was scrubbed... URL: From sidney_f_monteiro at hotmail.com Mon Jan 26 07:32:32 2026 From: sidney_f_monteiro at hotmail.com (Sidney Monteiro) Date: Mon, 26 Jan 2026 07:32:32 +0000 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: Normally I just skip reading these suggestions but now and then there are fundamentally egregious ones that demand a very loud NO. There are no use cases begging for this. Despite the long pitch by the author, there are NO advantages/benefits in doing this. ZERO, ZILCH. And a mamoth-sized list of drawbacks. This is a non solution to a non problem and it creates work and problems. I'd kick this back to the author to gain experience in writing with the idioms and the spirit of the language as using block scope is a basic skill. Having references to other forums does not improve matters. forget this. pun intended. -------- Original message -------- From: Marek Kozie? Date: 1/26/26 01:32 (GMT-05:00) To: amber-dev at openjdk.org Subject: Explicit end-of-use: the 'forget' keyword Hi, Java lacks a way to express intentional end-of-use. This leads to: - accidental reuse of values that are no longer semantically valid - unclear intent during code review - refactoring hazards when scopes expand I?m interested in revisiting and refining this concept (raw form was discussed during Project Coin in 2009). The goal is to better understand the problem space so that, if and when the benefits clearly justify it, a well-formed direction could be considered. OVERVIEW FEATURE SUMMARY: The forget keyword prevents further access to a variable, parameter, or field within a defined scope, attempts to access the variable in forbidden scope will result in a compile-time error. MAJOR ADVANTAGE: This change makes variable and resource lifetimes explicit and compiler-enforced, improving code clarity and predictability. MAJOR BENEFITS: Allows explicitly removing a variable from the active context (in terms of accessibility), which is currently: impossible for final variables (only comments can be used), impossible for method parameters (except assigning null to non-final references), impossible for fields, cumbersome for local variables, requiring artificial blocks (extra lines and indentation). Makes it possible to explicitly declare that a variable should no longer be used or no longer represents valid data in the current scope, including cases where continued use would be undesirable or error-prone (like when there is method created for this purpose). Preserves code quality over time, avoiding degradation caused by = null assignments, comments-only conventions, or artificial scoping blocks. MAJOR DISADVANTAGE: The introduction of a new reserved keyword introduces potential source incompatibilities with existing codebases that define identifiers named forget. ALTERNATIVES: Java currently provides only scope-based lifetime control (blocks and try-with-resources). It lacks a general, explicit, and compiler-enforced mechanism to terminate variable usability at an arbitrary point within an existing scope. EXAMPLES Simple and Advanced Examples: forget var; // Variable is forgotten for the remainder of the current block or method (default behavior) forget var : scope; // Variable is forgotten inside the entire scope statement where scope could potentially be : if, for, while, try, label, static, method forget (var1, var2, ...); // Specified variables are forgotten for the remainder of the current block forget this.field; // Specified field are forgotten for the remainder of the current block forget (var1, var2, ...) { /* code */ }; // Specified variables are forgotten only inside the enclosed block void handleRequest(String request, String token) { if (!isTokenValid(token)) { throw new SecurityException("Invalid token"); } authorize(request, token); forget token /* used & contains sensitive info */; process(request); logger.debug("token was: " + token); // Compile-time error: 'token' has been forgotten and cannot be used } public Product(String name) { // constructor this.name = name.trim().intern(); forget name; // From now on, only use 'this.name'! // other constructor commands... if (isDuplicate(this.name)) { ... } // Always canonical, never raw input if (isDuplicate(name)) { ... } // Compile-time ERROR! } // * Forces usage of the correctly prepared value (this.name) only. // * Prevents code drift, maintenance bugs, or copy-paste errors that reference the raw parameter. // * Makes the constructor safer: no risk of mismatches or inconsistent logic. // * Reads as a contract: "from here on, don't touch the original argument!" Next Version Examples: forget ClassName.field; forget variable.field; forget (!variable); // Limit allowed variables to ones that are directly specified DETAILS SPECIFICATION: forget [ Identifier | ( IdentifierList ) ] [ : Scope | { block }]; IdentifierList: Identifier {, Identifier} Identifier: [ VariableIdentifier | this.FieldIdentifier ] The forget statement forbids any further use of the specified identifier in all subsequent expressions and statements within the declared scope in which the identifier would normally be accessible. COMPILATION: The variable is not physically erased (except it may be if not a field); rather, it is protected from any further access after the forget statement. Additionally, retaining the variable in the scope (but inaccessible) prevents situations where a developer tries to create a new variable with the same name after removing the forget statement, thereby enforcing consistent usage and avoiding hidden bugs. TESTING: Testing the forget statement is equivalent to testing variable scope after exiting a block?the variable becomes inaccessible. For fields, forget enforces access control, ensuring the field cannot be used within the specified scope for the remainder of its block or method.LIBRARY COMPATIBILITY The introduction of a new keyword (forget) may cause conflicts in codebases where forget is already used as an identifier. There are no other compatibility impacts. REFERENCES Current draft ? https://lasu2string.blogspot.com/2026/01/Java-forget-keyword.html Reddit discussion ? https://www.reddit.com/r/java/comments/1qhhf9y/proposal_introducing_the_forget_keyword_in_java/ Draft from 2009 ? https://lasu2string.blogspot.com/2009/03/forget-keyword-proposal_27.html Project coin 2009 discussion entry ? https://mail.openjdk.org/pipermail/coin-dev/2009-March/001093.html JDK-6189163 ? https://bugs.openjdk.org/browse/JDK-6189163 PROBLEMS Backward Compatibility: Introducing forget as a new reserved keyword will cause compilation errors in existing code that already uses forget as an identifier (variable, method, class, etc). Tooling Lag: IDEs, static analysis tools, and debuggers must all be updated to handle the new keyword and its effects on variable visibility. Code Readability: Misuse or overuse of forget could make code harder to maintain or follow if not used judiciously, especially if variables are forgotten in non-obvious places. Teaching and Onboarding: This feature introduces a new concept that must be documented and taught to all developers, which can increase the learning curve for Java. Migration Complexity: Automatic migration of legacy code may be challenging, particularly for projects that rely on forget as an existing identifier or which have established conventions for variable lifetime. Interaction with Scoping and Shadowing: The detailed behavior when variables are forgotten, shadowed, or reintroduced in inner scopes may lead to confusion and subtle bugs if not carefully specified and implemented. Reflection and Debugging: While reflective APIs themselves are not impacted, developers may be surprised by the presence of variables at runtime (for debugging or reflection) that are "forgotten" in the source code. Consistency Across Language Features: Defining consistent behavior for forget in new contexts (e.g., lambdas, anonymous classes, record classes) may require extra specification effort. Edge Cases and Specification Complexity: Fully specifying the semantics of forget for all cases?including fields, parameters, captured variables in inner/nested classes, and interaction with try/catch/finally?may be complex. Unused Feature Risk: There is a risk that the forget keyword will see little real-world use, or will be misunderstood, if not supported and encouraged by frameworks or coding standards. THE RIGHT AND THE WRONG WAY During the discussion, it became clear that the main conception of usage was not approached from the right angle. For example, the request JDK-6189163: New language feature: undeclare operator made a similar suggestion. Unfortunately, much of the focus was on "destroying" variables as a matter of code complexity and maintenance. This focus is problematic for several reasons: The compiler can already release variables when they are no longer needed. Focusing on disposing bytes of data is really a bad step as it shouldn't be a concern in the first place. Focusing on variable usability in this way actually decreases maintainability, since both current and future modifications would require additional checks to unlock variables or identify where a variable has been "undeclared" in the middle of code. This complicates code changes unnecessarily. At the same time, we should highlight that many language features are, to some extent, syntactic sugar from a usability perspective: Block-based local variable scoping - Enhanced for Loop - Final guard - Generics - Lambdas - Records - Switch Expressions - Text Blocks - Try-With-Resources - ... and many more Each of these features can absolutely be used in the wrong way, which may decrease code quality. On the other hand, when used properly, they can significantly reduce cognitive load and the time required for analysis, refactoring, and modification - and this path the change will aim to follow. So we should focus on situations where forget is actually beneficial in the short or long term: When code is actively being developed -especially complex one - there are situations where removing unnecessary variables from the context is beneficial. It's entirely possible that in the final version these variables will be placed into proper scopes with no need of forget, but until then forget would serve an important purpose during development. When modifying long or complex code?especially if an issue arises from the incorrect reuse of a variable: We could safeguard the variable with a comment explaining its intended use and the change made. This targeted fix is preferable to an unannotated quick fix (which leaves confusion for future maintainers) or an extensive refactor, which could disrupt code history and introduce new errors. Writing security-sensitive code where resources need to be released in a specific order that cannot be enforced using classic block structures. Explicitly excluding class variables from the current method scope when ussage could be harmfull(for example in efficiency aspect), helping to prevent accidental usage and improve code clarity. SIMPLIFIED VERSION Problem: Handling variable "forgetting" via scope control can introduce unnecessary complexity. To address this, an alternative version without scope customization could be considered. Simplified Solution: Instead of customizing scopes, forget could apply to the method/block scope. Caveat: It does NOT work for mutually exclusive scopes like parallel branches (if-else, switch-case). In those, you must restate forget in each branch. void handleRequest(String request, String token) { if (!isTokenValid(token)) { throw new SecurityException("Invalid token"); } if (forceLogOut()){ logOut(request, token); forget token; // prevent usage in the rest of this block and after the if-else ... } else { authorize(request, token); forget token; // 'forget' needs to be restated here ... } logger.debug("token was: " + token); // Compile-time error! } This approach is more rigid and resistant to refactoring mistakes if block scopes change. If more flexibility is needed, a more complex form like forget var : label; (for better control) can be introduced as an advanced feature. SUMMARY The forget keyword represents a natural evolution of Java's commitment to clear, explicit, and compiler-enforced language rules. By allowing developers to mark variables, parameters, or fields as no longer usable within a defined scope, forget makes variable lifetimes and resource management visible and deliberate. This approach eliminates ambiguity in code, prevents accidental misuse, and reinforces Java?s tradition of making correctness and safety a language guarantee, not just a convention. Like Java?s robust type system and scoping mechanisms, forget enhances code clarity, maintainability, and reliability. -- Greetings Marek Kozie? ( Lasu ) -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jan 26 07:59:51 2026 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 26 Jan 2026 08:59:51 +0100 (CET) Subject: [SPAM] Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: <459643006.25842682.1769414391234.JavaMail.zimbra@univ-eiffel.fr> Hello Marek, usually, if you are not able to track the lifetime of a variable by just reading the code, it means that the code as to be changed/refactored, not complexified by adding yet another keyword. For local variables, in any C like language, if you want to reduce the scope of a variable, just add a block void foo() { int a = 3; { // reduce the visibility of all the variable declared inside that block int b = 4; } // b is not accessible here } Also, in JavaScript, there is an operator "delete" [1] which has very a similar semantics to what you are proposing, that was deprecated when the strict mode was introduced (EcmaScript 5, 2015). regards, R?mi [1] [ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete? ] PS: you are wrong that in a constructor once you have initialized a field, you do not want to access to the local variable/parameter used for initializing that variable. (see JEP 513: [ https://openjdk.org/jeps/513 | https://openjdk.org/jeps/513 ] ) > From: "Marek Kozie?" > To: "amber-dev" > Sent: Monday, January 26, 2026 7:31:31 AM > Subject: [SPAM] Explicit end-of-use: the 'forget' keyword > Hi, > Java lacks a way to express intentional end-of-use. > This leads to: > - accidental reuse of values that are no longer semantically valid > - unclear intent during code review > - refactoring hazards when scopes expand > I?m interested in revisiting and refining this concept (raw form was discussed > during Project Coin in 2009). The goal is to better understand the problem > space so that, if and when the benefits clearly justify it, a well-formed > direction could be considered. > OVERVIEW > FEATURE SUMMARY: > The forget keyword prevents further access to a variable, parameter, or field > within a defined scope, attempts to access the variable in forbidden scope will > result in a compile-time error. > MAJOR ADVANTAGE: > This change makes variable and resource lifetimes explicit and > compiler-enforced, improving code clarity and predictability. > MAJOR BENEFITS: > Allows explicitly removing a variable from the active context (in terms of > accessibility), which is currently: > impossible for final variables (only comments can be used), > impossible for method parameters (except assigning null to non-final > references), > impossible for fields, > cumbersome for local variables, requiring artificial blocks (extra lines and > indentation). > Makes it possible to explicitly declare that a variable should no longer be used > or no longer represents valid data in the current scope, including cases where > continued use would be undesirable or error-prone (like when there is method > created for this purpose). > Preserves code quality over time, avoiding degradation caused by = null > assignments, comments-only conventions, or artificial scoping blocks. > MAJOR DISADVANTAGE: > The introduction of a new reserved keyword introduces potential source > incompatibilities with existing codebases that define identifiers named forget. > ALTERNATIVES: > Java currently provides only scope-based lifetime control (blocks and > try-with-resources). It lacks a general, explicit, and compiler-enforced > mechanism to terminate variable usability at an arbitrary point within an > existing scope. > EXAMPLES > Simple and Advanced Examples: > forget var; // Variable is forgotten for the remainder of the current block or > method (default behavior) > forget var : scope; // Variable is forgotten inside the entire scope statement > where scope could potentially be : if, for, while, try, label, static, method > forget (var1, var2, ...); // Specified variables are forgotten for the remainder > of the current block > forget this.field; // Specified field are forgotten for the remainder of the > current block > forget (var1, var2, ...) { /* code */ }; // Specified variables are forgotten > only inside the enclosed block > void handleRequest(String request, String token) { if (!isTokenValid(token)) { > throw new SecurityException("Invalid token"); > } > authorize(request, token); > forget token /* used & contains sensitive info */; > process(request); > logger.debug("token was: " + token); // Compile-time error: 'token' has been > forgotten and cannot be used > } > public Product(String name) { // constructor > [ http://this.name/ | this.name ] = name.trim().intern(); > forget name; // From now on, only use ' [ http://this.name/ | this.name ] '! > // other constructor commands... > if (isDuplicate( [ http://this.name/ | this.name ] )) { ... } // Always > canonical, never raw input > if (isDuplicate(name)) { ... } // Compile-time ERROR! > } > // * Forces usage of the correctly prepared value ( [ http://this.name/ | > this.name ] ) only. > // * Prevents code drift, maintenance bugs, or copy-paste errors that reference > the raw parameter. > // * Makes the constructor safer: no risk of mismatches or inconsistent logic. > // * Reads as a contract: "from here on, don't touch the original argument!" > Next Version Examples: > forget ClassName.field; > forget variable.field; > forget (!variable); // Limit allowed variables to ones that are directly > specified > DETAILS > SPECIFICATION: > forget [ Identifier | ( IdentifierList ) ] [ : Scope | { block }]; > IdentifierList: > Identifier {, Identifier} > Identifier: > [ VariableIdentifier | this.FieldIdentifier ] > The forget statement forbids any further use of the specified identifier in all > subsequent expressions and statements within the declared scope in which the > identifier would normally be accessible. > COMPILATION: > The variable is not physically erased (except it may be if not a field); rather, > it is protected from any further access after the forget statement. > Additionally, retaining the variable in the scope (but inaccessible) prevents > situations where a developer tries to create a new variable with the same name > after removing the forget statement, thereby enforcing consistent usage and > avoiding hidden bugs. > TESTING: > Testing the forget statement is equivalent to testing variable scope after > exiting a block?the variable becomes inaccessible. For fields, forget enforces > access control, ensuring the field cannot be used within the specified scope > for the remainder of its block or method.LIBRARY > COMPATIBILITY > The introduction of a new keyword (forget) may cause conflicts in codebases > where forget is already used as an identifier. There are no other compatibility > impacts. > REFERENCES > Current draft ? [ > https://lasu2string.blogspot.com/2026/01/Java-forget-keyword.html | > https://lasu2string.blogspot.com/2026/01/Java-forget-keyword.html ] > Reddit discussion ? [ > https://www.reddit.com/r/java/comments/1qhhf9y/proposal_introducing_the_forget_keyword_in_java/ > | > https://www.reddit.com/r/java/comments/1qhhf9y/proposal_introducing_the_forget_keyword_in_java/ > ] > Draft from 2009 ? [ > https://lasu2string.blogspot.com/2009/03/forget-keyword-proposal_27.html | > https://lasu2string.blogspot.com/2009/03/forget-keyword-proposal_27.html ] > Project coin 2009 discussion entry ? [ > https://mail.openjdk.org/pipermail/coin-dev/2009-March/001093.html | > https://mail.openjdk.org/pipermail/coin-dev/2009-March/001093.html ] > JDK-6189163 ? [ https://bugs.openjdk.org/browse/JDK-6189163 | > https://bugs.openjdk.org/browse/JDK-6189163 ] > PROBLEMS > Backward Compatibility: Introducing forget as a new reserved keyword will cause > compilation errors in existing code that already uses forget as an identifier > (variable, method, class, etc). > Tooling Lag: IDEs, static analysis tools, and debuggers must all be updated to > handle the new keyword and its effects on variable visibility. > Code Readability: Misuse or overuse of forget could make code harder to maintain > or follow if not used judiciously, especially if variables are forgotten in > non-obvious places. > Teaching and Onboarding: This feature introduces a new concept that must be > documented and taught to all developers, which can increase the learning curve > for Java. > Migration Complexity: Automatic migration of legacy code may be challenging, > particularly for projects that rely on forget as an existing identifier or > which have established conventions for variable lifetime. > Interaction with Scoping and Shadowing: The detailed behavior when variables are > forgotten, shadowed, or reintroduced in inner scopes may lead to confusion and > subtle bugs if not carefully specified and implemented. > Reflection and Debugging: While reflective APIs themselves are not impacted, > developers may be surprised by the presence of variables at runtime (for > debugging or reflection) that are "forgotten" in the source code. > Consistency Across Language Features: Defining consistent behavior for forget in > new contexts (e.g., lambdas, anonymous classes, record classes) may require > extra specification effort. > Edge Cases and Specification Complexity: Fully specifying the semantics of > forget for all cases?including fields, parameters, captured variables in > inner/nested classes, and interaction with try/catch/finally?may be complex. > Unused Feature Risk: There is a risk that the forget keyword will see little > real-world use, or will be misunderstood, if not supported and encouraged by > frameworks or coding standards. > THE RIGHT AND THE WRONG WAY > During the discussion, it became clear that the main conception of usage was not > approached from the right angle. > For example, the request JDK-6189163: New language feature: undeclare operator > made a similar suggestion. Unfortunately, much of the focus was on "destroying" > variables as a matter of code complexity and maintenance. This focus is > problematic for several reasons: > The compiler can already release variables when they are no longer needed. > Focusing on disposing bytes of data is really a bad step as it shouldn't be a > concern in the first place. > Focusing on variable usability in this way actually decreases maintainability, > since both current and future modifications would require additional checks to > unlock variables or identify where a variable has been "undeclared" in the > middle of code. This complicates code changes unnecessarily. > At the same time, we should highlight that many language features are, to some > extent, syntactic sugar from a usability perspective: > Block-based local variable scoping > - Enhanced for Loop > - Final guard > - Generics > - Lambdas > - Records > - Switch Expressions > - Text Blocks > - Try-With-Resources > - ... and many more > Each of these features can absolutely be used in the wrong way, which may > decrease code quality. > On the other hand, when used properly, they can significantly reduce cognitive > load and the time required for analysis, refactoring, and modification - and > this path the change will aim to follow. > So we should focus on situations where forget is actually beneficial in the > short or long term: > When code is actively being developed -especially complex one - there are > situations where removing unnecessary variables from the context is beneficial. > It's entirely possible that in the final version these variables will be placed > into proper scopes with no need of forget, but until then forget would serve an > important purpose during development. > When modifying long or complex code?especially if an issue arises from the > incorrect reuse of a variable: We could safeguard the variable with a comment > explaining its intended use and the change made. This targeted fix is > preferable to an unannotated quick fix (which leaves confusion for future > maintainers) or an extensive refactor, which could disrupt code history and > introduce new errors. > Writing security-sensitive code where resources need to be released in a > specific order that cannot be enforced using classic block structures. > Explicitly excluding class variables from the current method scope when ussage > could be harmfull(for example in efficiency aspect), helping to prevent > accidental usage and improve code clarity. > SIMPLIFIED VERSION > Problem: Handling variable "forgetting" via scope control can introduce > unnecessary complexity. To address this, an alternative version without scope > customization could be considered. > Simplified Solution: > Instead of customizing scopes, forget could apply to the method/block scope. > Caveat: It does NOT work for mutually exclusive scopes like parallel branches > (if-else, switch-case). In those, you must restate forget in each branch. > void handleRequest(String request, String token) { > if (!isTokenValid(token)) { > throw new SecurityException("Invalid token"); > } > if (forceLogOut()){ > logOut(request, token); > forget token; // prevent usage in the rest of this block and after the if-else > ... > } else { > authorize(request, token); > forget token; // 'forget' needs to be restated here > ... > } > logger.debug("token was: " + token); // Compile-time error! > } > This approach is more rigid and resistant to refactoring mistakes if block > scopes change. > If more flexibility is needed, a more complex form like forget var : label; (for > better control) can be introduced as an advanced feature. > SUMMARY > The forget keyword represents a natural evolution of Java's commitment to clear, > explicit, and compiler-enforced language rules. By allowing developers to mark > variables, parameters, or fields as no longer usable within a defined scope, > forget makes variable lifetimes and resource management visible and deliberate. > This approach eliminates ambiguity in code, prevents accidental misuse, and > reinforces Java?s tradition of making correctness and safety a language > guarantee, not just a convention. Like Java?s robust type system and scoping > mechanisms, forget enhances code clarity, maintainability, and reliability. > -- > Greetings > Marek Kozie? ( Lasu ) -------------- next part -------------- An HTML attachment was scrubbed... URL: From rleach at rleach.id.au Mon Jan 26 09:09:08 2026 From: rleach at rleach.id.au (Ryan Leach) Date: Mon, 26 Jan 2026 19:39:08 +1030 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Message-ID: Whilst I agree with Brian, is this not a smell though if it can't be restricted in this way, that the proposed additions aren't flexible enough to ensure program correctness in what's surely going to be a fairly common pattern? Maybe withers shouldn't just be a syntactic optimisation for exactly this reason. -- Ryan On Mon, 26 Jan 2026, 5:40?am Brian Goetz, wrote: > The important mental model here is that a reconstruction (`with`) > expression is "just" a syntactic optimization for: > > - destructure with the canonical deconstruction pattern > - mutate the components > - reconstruct with the primary constructor > > So the root problem here is not the reconstruction expression; if you can > bork up your application state with a reconstruction expression, you can > bork it up without one. > > Primary constructors can enforce invariants _on_ or _between_ components, > such as: > > record Rational(int num, int denom) { > Rational { if (denom == 0) throw ... } > } > > or > > record Range(int lo, int hi) { > Range { if (lo > hi) throw... } > } > > What they can't do is express invariants between the record / carrier > state and "the rest of the system", because they are supposed to be simple > data carriers, not serialized references to some external system. A > class that models a database row in this way is complecting entity state > with an external entity id. By modeling in this way, you have explicitly > declared that > > rec with { dbId++ } > > *is explicitly OK* in your system; that the components of the record can > be freely combined in any way (modulo enforced cross-component > invariants). And there are systems in which this is fine! But you're > imagining (correctly) that this modeling technique will be used in systems > in which this is not fine. > > The main challenge here is that developers will be so attracted to the > syntactic concision that they will willfully ignore the semantic > inconsistencies they are creating. > > > > > On 1/25/2026 1:37 PM, Andy Gegg wrote: > > Hello, > I apologise for coming late to the party here - Records have been of > limited use to me but Mr Goetz's email on carrier classes is something that > would be very useful so I've been thinking about the consequences. > > Since carrier classes and records are for data, in a database application > somewhere or other you're going to get database ids in records: > record MyRec(int dbId, String name,...) > > While everything is immutable this is fine but JEP 468 opens up the > possibility of mutation: > > MyRec rec = readDatabase(...); > rec = rec with {name="...";}; > writeDatabase(rec); > > which is absolutely fine and what an application wants to do. But: > MyRec rec = readDatabase(...); > rec = rec with {dbId++;}; > writeDatabase(rec); > > is disastrous. There's no way the canonical constructor invoked from > 'with' can detect stupidity nor can whatever the database access layer does. > > In the old days, the lack of a 'setter' would usually prevent stupid code > - the above could be achieved, obviously, but the code is devious enough to > make people stop and think (one hopes). > > Here there is nothing to say "do not update this!!!" except code comments, > JavaDoc and naming conventions. > > It's not always obvious which fields may or may not be changed in the > application. > > record MyRec(int dbId, int fatherId,...) > probably doesn't want > rec = rec with { fatherId = ... } > > but a HR application will need to be able to do: > > record MyRec(int dbId, int departmentId, ...); > ... > rec = rec with { departmentId = newDept; }; > > Clearly, people can always write stupid code (guilty...) and the current > state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, > ...);) which is enough to stop people using records here but carrier > classes will be very tempting and that brings derived creation back to the > fore. > > It's not just database ids which might need restricting from update, e.g. > timestamps (which are better done in the database layer) and no doubt > different applications will have their own business case restrictions. > > Thank you for your time, > Andy Gegg > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Jan 26 09:18:19 2026 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 26 Jan 2026 10:18:19 +0100 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Message-ID: Hello! It's interesting that when language designers make the code easier to write, somebody may complain that it's too easy :-) I think it's a perfect place for static analysis tooling. One may invent an annotation like `@NonUpdatable` with the `RECORD_COMPONENT` target and use it on such fields, then create an annotation processor (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that will check the violations and fail the build if there are any. Adding such a special case to the language specification would be an overcomplication. With best regards, Tagir Valeev. On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz wrote: > The important mental model here is that a reconstruction (`with`) > expression is "just" a syntactic optimization for: > > - destructure with the canonical deconstruction pattern > - mutate the components > - reconstruct with the primary constructor > > So the root problem here is not the reconstruction expression; if you can > bork up your application state with a reconstruction expression, you can > bork it up without one. > > Primary constructors can enforce invariants _on_ or _between_ components, > such as: > > record Rational(int num, int denom) { > Rational { if (denom == 0) throw ... } > } > > or > > record Range(int lo, int hi) { > Range { if (lo > hi) throw... } > } > > What they can't do is express invariants between the record / carrier > state and "the rest of the system", because they are supposed to be simple > data carriers, not serialized references to some external system. A > class that models a database row in this way is complecting entity state > with an external entity id. By modeling in this way, you have explicitly > declared that > > rec with { dbId++ } > > *is explicitly OK* in your system; that the components of the record can > be freely combined in any way (modulo enforced cross-component > invariants). And there are systems in which this is fine! But you're > imagining (correctly) that this modeling technique will be used in systems > in which this is not fine. > > The main challenge here is that developers will be so attracted to the > syntactic concision that they will willfully ignore the semantic > inconsistencies they are creating. > > > > > On 1/25/2026 1:37 PM, Andy Gegg wrote: > > Hello, > I apologise for coming late to the party here - Records have been of > limited use to me but Mr Goetz's email on carrier classes is something that > would be very useful so I've been thinking about the consequences. > > Since carrier classes and records are for data, in a database application > somewhere or other you're going to get database ids in records: > record MyRec(int dbId, String name,...) > > While everything is immutable this is fine but JEP 468 opens up the > possibility of mutation: > > MyRec rec = readDatabase(...); > rec = rec with {name="...";}; > writeDatabase(rec); > > which is absolutely fine and what an application wants to do. But: > MyRec rec = readDatabase(...); > rec = rec with {dbId++;}; > writeDatabase(rec); > > is disastrous. There's no way the canonical constructor invoked from > 'with' can detect stupidity nor can whatever the database access layer does. > > In the old days, the lack of a 'setter' would usually prevent stupid code > - the above could be achieved, obviously, but the code is devious enough to > make people stop and think (one hopes). > > Here there is nothing to say "do not update this!!!" except code comments, > JavaDoc and naming conventions. > > It's not always obvious which fields may or may not be changed in the > application. > > record MyRec(int dbId, int fatherId,...) > probably doesn't want > rec = rec with { fatherId = ... } > > but a HR application will need to be able to do: > > record MyRec(int dbId, int departmentId, ...); > ... > rec = rec with { departmentId = newDept; }; > > Clearly, people can always write stupid code (guilty...) and the current > state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, > ...);) which is enough to stop people using records here but carrier > classes will be very tempting and that brings derived creation back to the > fore. > > It's not just database ids which might need restricting from update, e.g. > timestamps (which are better done in the database layer) and no doubt > different applications will have their own business case restrictions. > > Thank you for your time, > Andy Gegg > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Jan 26 09:22:28 2026 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 26 Jan 2026 10:22:28 +0100 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: Hello! I think this violates the structured programming principles, as it makes the scopes non-nested anymore. This would complicate code understanding and refactoring (like extract method or inline method). Finally, I believe, almost nobody would use this feature because the code works fine without it. I agree with Remi: if you want to restrict the scope, either create a block, or extract a method. With best regards, Tagir Valeev On Mon, Jan 26, 2026 at 7:32?AM Marek Kozie? wrote: > Hi, > > Java lacks a way to express intentional end-of-use. > > This leads to: > - accidental reuse of values that are no longer semantically valid > - unclear intent during code review > - refactoring hazards when scopes expand > > I?m interested in revisiting and refining this concept (raw form was > discussed during Project Coin in 2009). The goal is to better understand > the problem space so that, if and when the benefits clearly justify it, a > well-formed direction could be considered. > > > > OVERVIEW > > FEATURE SUMMARY: > The forget keyword prevents further access to a variable, parameter, or > field within a defined scope, attempts to access the variable in forbidden > scope will result in a compile-time error. > > MAJOR ADVANTAGE: > This change makes variable and resource lifetimes explicit and > compiler-enforced, improving code clarity and predictability. > > MAJOR BENEFITS: > Allows explicitly removing a variable from the active context (in terms of > accessibility), which is currently: > > impossible for final variables (only comments can be used), > impossible for method parameters (except assigning null to non-final > references), > impossible for fields, > cumbersome for local variables, requiring artificial blocks (extra lines > and indentation). > > Makes it possible to explicitly declare that a variable should no longer > be used or no longer represents valid data in the current scope, including > cases where continued use would be undesirable or error-prone (like when > there is method created for this purpose). > Preserves code quality over time, avoiding degradation caused by = null > assignments, comments-only conventions, or artificial scoping blocks. > > MAJOR DISADVANTAGE: > The introduction of a new reserved keyword introduces potential source > incompatibilities with existing codebases that define identifiers named > forget. > > ALTERNATIVES: > Java currently provides only scope-based lifetime control (blocks and > try-with-resources). It lacks a general, explicit, and compiler-enforced > mechanism to terminate variable usability at an arbitrary point within an > existing scope. > > EXAMPLES > > Simple and Advanced Examples: > forget var; // Variable is forgotten for the remainder of the current > block or method (default behavior) > forget var : scope; // Variable is forgotten inside the entire scope > statement > > where scope could potentially be : if, for, while, try, label, static, > method > > forget (var1, var2, ...); // Specified variables are forgotten for the > remainder of the current block > > forget this.field; // Specified field are forgotten for the remainder of > the current block > > forget (var1, var2, ...) { /* code */ }; // Specified variables are > forgotten only inside the enclosed block > > void handleRequest(String request, String token) { if > (!isTokenValid(token)) { > throw new SecurityException("Invalid token"); > } > > authorize(request, token); > > forget token /* used & contains sensitive info */; > > process(request); > > logger.debug("token was: " + token); // Compile-time error: 'token' has > been forgotten and cannot be used > } > > public Product(String name) { // constructor > this.name = name.trim().intern(); > forget name; // From now on, only use 'this.name'! > > // other constructor commands... > > if (isDuplicate(this.name)) { ... } // Always canonical, never raw > input > if (isDuplicate(name)) { ... } // Compile-time ERROR! > } > > // * Forces usage of the correctly prepared value (this.name) only. > // * Prevents code drift, maintenance bugs, or copy-paste errors that > reference the raw parameter. > // * Makes the constructor safer: no risk of mismatches or inconsistent > logic. > // * Reads as a contract: "from here on, don't touch the original > argument!" > Next Version Examples: > forget ClassName.field; > forget variable.field; > forget (!variable); // Limit allowed variables to ones that are directly > specified > > DETAILS > > SPECIFICATION: > forget [ Identifier | ( IdentifierList ) ] [ : Scope | { block }]; > > IdentifierList: > Identifier {, Identifier} > > Identifier: > [ VariableIdentifier | this.FieldIdentifier ] > > The forget statement forbids any further use of the specified identifier > in all subsequent expressions and statements within the declared scope in > which the identifier would normally be accessible. > > COMPILATION: > The variable is not physically erased (except it may be if not a field); > rather, it is protected from any further access after the forget statement. > Additionally, retaining the variable in the scope (but inaccessible) > prevents situations where a developer tries to create a new variable with > the same name after removing the forget statement, thereby enforcing > consistent usage and avoiding hidden bugs. > > TESTING: > Testing the forget statement is equivalent to testing variable scope after > exiting a block?the variable becomes inaccessible. For fields, forget > enforces access control, ensuring the field cannot be used within the > specified scope for the remainder of its block or method.LIBRARY > > COMPATIBILITY > The introduction of a new keyword (forget) may cause conflicts in > codebases where forget is already used as an identifier. There are no other > compatibility impacts. > > REFERENCES > Current draft ? > https://lasu2string.blogspot.com/2026/01/Java-forget-keyword.html > Reddit discussion ? > https://www.reddit.com/r/java/comments/1qhhf9y/proposal_introducing_the_forget_keyword_in_java/ > Draft from 2009 ? > https://lasu2string.blogspot.com/2009/03/forget-keyword-proposal_27.html > Project coin 2009 discussion entry ? > https://mail.openjdk.org/pipermail/coin-dev/2009-March/001093.html > JDK-6189163 ? https://bugs.openjdk.org/browse/JDK-6189163 > > > PROBLEMS > > Backward Compatibility: Introducing forget as a new reserved keyword will > cause compilation errors in existing code that already uses forget as an > identifier (variable, method, class, etc). > Tooling Lag: IDEs, static analysis tools, and debuggers must all be > updated to handle the new keyword and its effects on variable visibility. > Code Readability: Misuse or overuse of forget could make code harder to > maintain or follow if not used judiciously, especially if variables are > forgotten in non-obvious places. > Teaching and Onboarding: This feature introduces a new concept that must > be documented and taught to all developers, which can increase the learning > curve for Java. > Migration Complexity: Automatic migration of legacy code may be > challenging, particularly for projects that rely on forget as an existing > identifier or which have established conventions for variable lifetime. > Interaction with Scoping and Shadowing: The detailed behavior when > variables are forgotten, shadowed, or reintroduced in inner scopes may lead > to confusion and subtle bugs if not carefully specified and implemented. > Reflection and Debugging: While reflective APIs themselves are not > impacted, developers may be surprised by the presence of variables at > runtime (for debugging or reflection) that are "forgotten" in the source > code. > Consistency Across Language Features: Defining consistent behavior for > forget in new contexts (e.g., lambdas, anonymous classes, record classes) > may require extra specification effort. > Edge Cases and Specification Complexity: Fully specifying the semantics of > forget for all cases?including fields, parameters, captured variables in > inner/nested classes, and interaction with try/catch/finally?may be complex. > Unused Feature Risk: There is a risk that the forget keyword will see > little real-world use, or will be misunderstood, if not supported and > encouraged by frameworks or coding standards. > > THE RIGHT AND THE WRONG WAY > > During the discussion, it became clear that the main conception of usage > was not approached from the right angle. > For example, the request JDK-6189163: New language feature: undeclare > operator made a similar suggestion. Unfortunately, much of the focus was on > "destroying" variables as a matter of code complexity and maintenance. This > focus is problematic for several reasons: > > The compiler can already release variables when they are no longer needed. > Focusing on disposing bytes of data is really a bad step as it shouldn't > be a concern in the first place. > Focusing on variable usability in this way actually decreases > maintainability, since both current and future modifications would require > additional checks to unlock variables or identify where a variable has been > "undeclared" in the middle of code. This complicates code changes > unnecessarily. > > > > At the same time, we should highlight that many language features are, to > some extent, syntactic sugar from a usability perspective: > > Block-based local variable scoping > - Enhanced for Loop > - Final guard > - Generics > - Lambdas > - Records > - Switch Expressions > - Text Blocks > - Try-With-Resources > - ... and many more > > Each of these features can absolutely be used in the wrong way, which may > decrease code quality. > On the other hand, when used properly, they can significantly reduce > cognitive load and the time required for analysis, refactoring, and > modification - and this path the change will aim to follow. > > So we should focus on situations where forget is actually beneficial in > the short or long term: > > When code is actively being developed -especially complex one - there are > situations where removing unnecessary variables from the context is > beneficial. It's entirely possible that in the final version these > variables will be placed into proper scopes with no need of forget, but > until then forget would serve an important purpose during development. > When modifying long or complex code?especially if an issue arises from the > incorrect reuse of a variable: We could safeguard the variable with a > comment explaining its intended use and the change made. This targeted fix > is preferable to an unannotated quick fix (which leaves confusion for > future maintainers) or an extensive refactor, which could disrupt code > history and introduce new errors. > Writing security-sensitive code where resources need to be released in a > specific order that cannot be enforced using classic block structures. > Explicitly excluding class variables from the current method scope when > ussage could be harmfull(for example in efficiency aspect), helping to > prevent accidental usage and improve code clarity. > > > > SIMPLIFIED VERSION > > Problem: Handling variable "forgetting" via scope control can introduce > unnecessary complexity. To address this, an alternative version without > scope customization could be considered. > > Simplified Solution: > > Instead of customizing scopes, forget could apply to the method/block > scope. > Caveat: It does NOT work for mutually exclusive scopes like parallel > branches (if-else, switch-case). In those, you must restate forget in each > branch. > > void handleRequest(String request, String token) { > if (!isTokenValid(token)) { > throw new SecurityException("Invalid token"); > } > if (forceLogOut()){ > logOut(request, token); > forget token; // prevent usage in the rest of this block and after > the if-else > ... > } else { > authorize(request, token); > forget token; // 'forget' needs to be restated here > ... > } > logger.debug("token was: " + token); // Compile-time error! > } > This approach is more rigid and resistant to refactoring mistakes if block > scopes change. > If more flexibility is needed, a more complex form like forget var : > label; (for better control) can be introduced as an advanced feature. > > SUMMARY > > The forget keyword represents a natural evolution of Java's commitment to > clear, explicit, and compiler-enforced language rules. By allowing > developers to mark variables, parameters, or fields as no longer usable > within a defined scope, forget makes variable lifetimes and resource > management visible and deliberate. This approach eliminates ambiguity in > code, prevents accidental misuse, and reinforces Java?s tradition of making > correctness and safety a language guarantee, not just a convention. Like > Java?s robust type system and scoping mechanisms, forget enhances code > clarity, maintainability, and reliability. > > -- > Greetings > Marek Kozie? ( Lasu ) > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Jan 26 10:23:08 2026 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 26 Jan 2026 11:23:08 +0100 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: As Archie mentioned, this is already possible in JDK 26 by selectively enabling some lint warnings as errors with `-Werror`. Not sure what more a javac plugin could do. As for having better toolings: This is the wrong list to request that. Those requests should go to the appropriate tool provider. Though as for Maven/Gradle plugins: There are already plenty of static analyzers and they usually can be configured to fail on various problems. A lot more than just "static" (in fact, there are a lot of far worse things you can do than calling static members on an object). I would recommend using these static analyzers until you can move to JDK 26+ (or if your favorite one doesn't support selective errors, then ask them for such a feature). Also, I don't see the "static" problem to be so extreme as to warrant a campaign on its own (especially not from the JDK team). They are bad, but if this is the biggest issue on projects you are working on, then I honestly envy you :) Attila Amazing Code ezt ?rta (id?pont: 2026. jan. 26., H, 4:38): > Hello all, > > Thanks again for the thoughtful discussion so far. Based on the thread I?d > like to suggest a practical, non-breaking way forward that gives teams the *option > *to enforce instance-qualified static access as a hard rule, while > preserving backward compatibility for everyone else. > > *Proposal (summary)* > Provide an opt-in enforcement stack consisting of: > 1. A reference *javac plugin* (e.g. StaticAccessEnforcer) that fails > compilation on instance-qualified static accesses when enabled; > 2. companion *Maven/Gradle plugins* that automatically enable the > plugin for projects that add the dependency or set a property; > 3. *IDE quick-fixers* and a command-line *codemod* to assist automated > migration; and > 4. when/if available, recommend using per-category Werror (e.g. > `-Werror:static`) for CI once OpenJDK supports it broadly. > > *Why this approach?* > - *No language break* ? default javac behavior remains unchanged. > - *Opt-in* ? new projects or teams can opt to treat the warning as an > error by adding a dependency/flag. > - *Ecosystem friendly* ? leverages javac plugin SPI, Checker > Framework/ErrorProne patterns, and IDE support. > :contentReference[oaicite:11]{index=11} > > *Implementation sketch* > - Build reference plugin using the `Plugin` SPI and publish as a Maven > artifact. Example invocation: `javac -Xplugin:StaticAccessEnforcer ...` or > via maven-compiler-plugin/Gradle compiler args. > :contentReference[oaicite:12]{index=12} > - Provide a `static-access-enforcer` Gradle/Maven plugin that wires the > `-Xplugin` or annotation processor automatically. > - Include automated codemod tooling (JavaParser/Eclipse JDT) and an > IntelliJ quick-fix to perform safe replacements. > - Plugin modes: `report` / `warn` / `error`, and support > `@SuppressWarnings("static")` or per-file exclusion. > > *Request* > If this sounds reasonable, I can prepare: > - a small reference implementation (javac plugin + sample Maven config), > and > - a short JEP-style note describing migration story and sample fixes. > > Thank you for considering this compromise ? it preserves Java?s > compatibility guarantees while giving teams a practical path to stronger, > enforced conventions. > > Kind regards, > Kamlesh > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amazingcodewithus at gmail.com Mon Jan 26 11:36:09 2026 From: amazingcodewithus at gmail.com (Amazing Code) Date: Mon, 26 Jan 2026 17:06:09 +0530 Subject: Incident Report 9079511: Java Language Enhancement: Disallow access to static members via object references In-Reply-To: References: Message-ID: Dear all, Thank you for the thoughtful discussion and for sharing your perspectives and references. Based on the feedback, it?s clear that turning instance-qualified static access into a hard language error is neither justified nor aligned with Java?s strong backward-compatibility guarantees. The existing mechanisms? -Xlint:static, IDE inspections, and especially the upcoming selective -Werror:static support in JDK 26?provide a practical and flexible way for teams to enforce stricter policies without impacting existing codebases. I also appreciate the clarification around tooling boundaries and the reminder that static analysis concerns are best handled by dedicated tools and IDEs rather than the compiler itself. Given this, I?m happy to close this proposal here. Thanks again for the detailed explanations and for taking the time to engage in the discussion?it was very informative. Best regards, Kamlesh On Mon, 26 Jan 2026 at 15:53, Attila Kelemen wrote: > As Archie mentioned, this is already possible in JDK 26 by selectively > enabling some lint warnings as errors with `-Werror`. Not sure what more a > javac plugin could do. > > As for having better toolings: This is the wrong list to request that. > Those requests should go to the appropriate tool provider. Though as for > Maven/Gradle plugins: There are already plenty of static analyzers and they > usually can be configured to fail on various problems. A lot more than just > "static" (in fact, there are a lot of far worse things you can do than > calling static members on an object). I would recommend using these static > analyzers until you can move to JDK 26+ (or if your favorite one doesn't > support selective errors, then ask them for such a feature). > > Also, I don't see the "static" problem to be so extreme as to warrant a > campaign on its own (especially not from the JDK team). They are bad, but > if this is the biggest issue on projects you are working on, then I > honestly envy you :) > > Attila > > Amazing Code ezt ?rta (id?pont: 2026. jan. > 26., H, 4:38): > >> Hello all, >> >> Thanks again for the thoughtful discussion so far. Based on the thread >> I?d like to suggest a practical, non-breaking way forward that gives teams >> the *option *to enforce instance-qualified static access as a hard rule, >> while preserving backward compatibility for everyone else. >> >> *Proposal (summary)* >> Provide an opt-in enforcement stack consisting of: >> 1. A reference *javac plugin* (e.g. StaticAccessEnforcer) that fails >> compilation on instance-qualified static accesses when enabled; >> 2. companion *Maven/Gradle plugins* that automatically enable the >> plugin for projects that add the dependency or set a property; >> 3. *IDE quick-fixers* and a command-line *codemod* to assist automated >> migration; and >> 4. when/if available, recommend using per-category Werror (e.g. >> `-Werror:static`) for CI once OpenJDK supports it broadly. >> >> *Why this approach?* >> - *No language break* ? default javac behavior remains unchanged. >> - *Opt-in* ? new projects or teams can opt to treat the warning as an >> error by adding a dependency/flag. >> - *Ecosystem friendly* ? leverages javac plugin SPI, Checker >> Framework/ErrorProne patterns, and IDE support. >> :contentReference[oaicite:11]{index=11} >> >> *Implementation sketch* >> - Build reference plugin using the `Plugin` SPI and publish as a Maven >> artifact. Example invocation: `javac -Xplugin:StaticAccessEnforcer ...` or >> via maven-compiler-plugin/Gradle compiler args. >> :contentReference[oaicite:12]{index=12} >> - Provide a `static-access-enforcer` Gradle/Maven plugin that wires the >> `-Xplugin` or annotation processor automatically. >> - Include automated codemod tooling (JavaParser/Eclipse JDT) and an >> IntelliJ quick-fix to perform safe replacements. >> - Plugin modes: `report` / `warn` / `error`, and support >> `@SuppressWarnings("static")` or per-file exclusion. >> >> *Request* >> If this sounds reasonable, I can prepare: >> - a small reference implementation (javac plugin + sample Maven >> config), and >> - a short JEP-style note describing migration story and sample fixes. >> >> Thank you for considering this compromise ? it preserves Java?s >> compatibility guarantees while giving teams a practical path to stronger, >> enforced conventions. >> >> Kind regards, >> Kamlesh >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From briangoetz at openjdk.org Mon Jan 26 14:16:11 2026 From: briangoetz at openjdk.org (Brian Goetz) Date: Mon, 26 Jan 2026 14:16:11 GMT Subject: [amber-docs] Withdrawn: Add "Data-Oriented Programming for Java: Beyond Records" In-Reply-To: References: Message-ID: On Sun, 18 Jan 2026 21:25:00 GMT, Nicolai Parlog wrote: > Add "Data-Oriented Programming for Java: Beyond Records" This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/amber-docs/pull/29 From duke at openjdk.org Mon Jan 26 14:16:15 2026 From: duke at openjdk.org (duke) Date: Mon, 26 Jan 2026 14:16:15 GMT Subject: git: openjdk/amber-docs: 2 new changesets Message-ID: Changeset: e0c9b37e Branch: master Author: Nicolai Parlog Committer: GitHub Date: 2026-01-26 15:13:17 +0000 URL: https://git.openjdk.org/amber-docs/commit/e0c9b37ec73281301a70dde07066373e21fe4f81 Add "Data-Oriented Programming for Java: Beyond Records" (#29) ! site/_index.md + site/design-notes/beyond-records.md Changeset: eb87d5bc Branch: master Author: Nicolai Parlog Committer: GitHub Date: 2026-01-26 15:13:39 +0000 URL: https://git.openjdk.org/amber-docs/commit/eb87d5bc0a62b7ec7a35c628c738d9296de4c528 Capitalize article titles according to Chicago style (#28) ! site/design-notes/on-ramp.md ! site/design-notes/patterns/towards-member-patterns.md From brian.goetz at oracle.com Mon Jan 26 14:25:16 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Jan 2026 09:25:16 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Message-ID: <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> > It's interesting that when language designers make the code easier to > write, somebody may complain that it's too easy :-) I too had that "you can't win" feeling :) I would recast the question here as "Can Java developers handle carrier classes".? Records are restricted enough to keep developers _mostly_ out of trouble, but the desire to believe that this is a syntactic and not semantic feature is a strong one, and given that many developers education about how the language works is limited to "what does IntelliJ suggest to me", may not even _realize_ they are giving into the dark side. I think it is worth working through the example here for "how would we recommend handling the case of a "active" row like this. > I think it's a perfect place for static analysis tooling. One may > invent an annotation like `@NonUpdatable` > with the `RECORD_COMPONENT` target and use it on such fields, then > create an annotation processor > (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that > will check the violations and fail the build if there are any. > Adding such a special case to the language specification would be an > overcomplication. > > With best regards, > Tagir Valeev. > > On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz > wrote: > > The important mental model here is that a reconstruction (`with`) > expression is "just" a syntactic optimization for: > > ?- destructure with the canonical deconstruction pattern > ?- mutate the components > ?- reconstruct with the primary constructor > > So the root problem here is not the reconstruction expression; if > you can bork up your application state with a reconstruction > expression, you can bork it up without one. > > Primary constructors can enforce invariants _on_ or _between_ > components, such as: > > ? ? record Rational(int num, int denom) { > ? ? ? ? Rational { if (denom == 0) throw ... } > ? ? } > > or > > ? ? record Range(int lo, int hi) { > ? ? ? ? Range { if (lo > hi) throw... } > ? ? } > > What they can't do is express invariants between the record / > carrier state and "the rest of the system", because they are > supposed to be simple data carriers, not serialized references to > some external system. A class that models a database row in this > way is complecting entity state with an external entity id.? By > modeling in this way, you have explicitly declared that > > ? ? rec with { dbId++ } > > *is explicitly OK* in your system; that the components of the > record can be freely combined in any way (modulo enforced > cross-component invariants).? And there are systems in which this > is fine!? But you're imagining (correctly) that this modeling > technique will be used in systems in which this is not fine. > > The main challenge here is that developers will be so attracted to > the syntactic concision that they will willfully ignore the > semantic inconsistencies they are creating. > > > > > On 1/25/2026 1:37 PM, Andy Gegg wrote: >> Hello, >> I apologise for coming late to the party here - Records have been >> of limited use to me but Mr Goetz's email on carrier classes is >> something that would be very useful so I've been thinking about >> the consequences. >> >> Since? carrier classes and records are for data, in a database >> application somewhere or other you're going to get database ids >> in records: >> record MyRec(int dbId, String name,...) >> >> While everything is immutable this is fine but JEP 468 opens up >> the possibility of mutation: >> >> MyRec rec?= readDatabase(...); >> rec = rec with {name="...";}; >> writeDatabase(rec); >> >> which is absolutely fine and what an application wants to do.? But: >> MyRec rec = readDatabase(...); >> rec = rec with {dbId++;}; >> writeDatabase(rec); >> >> is disastrous.? There's no way the canonical constructor invoked >> from 'with' can detect stupidity nor can whatever the database >> access layer does. >> >> In the old days, the lack of a 'setter' would usually prevent >> stupid code - the above could be achieved, obviously, but the >> code is devious enough to make people stop and think (one hopes). >> >> Here there is nothing to say "do not update this!!!" except code >> comments, JavaDoc and naming conventions. >> >> It's not always obvious which fields may or may not be changed in >> the application. >> >> record MyRec(int dbId, int fatherId,...) >> probably doesn't want >> rec = rec with { fatherId = ... } >> >> but a HR application will need to be able to do: >> >> record MyRec(int dbId, int departmentId, ...); >> ... >> rec = rec with { departmentId = newDept; }; >> >> Clearly, people can always write stupid code (guilty...) and the >> current state of play obviously allows the possibility (rec = new >> MyRec(rec.dbId++, ...);) which is enough to stop people using >> records here but carrier classes will be very tempting and that >> brings derived creation back to the fore. >> >> It's not just database ids which might need restricting from >> update, e.g. timestamps (which are better done in the database >> layer) and no doubt different applications will have their own >> business case restrictions. >> >> Thank you for your time, >> Andy Gegg >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Mon Jan 26 15:12:27 2026 From: ethan at mccue.dev (Ethan McCue) Date: Mon, 26 Jan 2026 10:12:27 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: My immediate thought (aside from imagining Brian trapped in an eternal version of that huffalumps and woozles scene from Winnie the Pooh, but it's all these emails) is that database entities aren't actually good candidates for "unconditional deconstruction" I think this because the act of getting the data from the db/persistence context is intrinsically fallible *and* attached to instance behavior; maybe we need to look forward to what the conditional deconstruction story would be? On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: > > > It's interesting that when language designers make the code easier to > write, somebody may complain that it's too easy :-) > > > I too had that "you can't win" feeling :) > > I would recast the question here as "Can Java developers handle carrier > classes". Records are restricted enough to keep developers _mostly_ out of > trouble, but the desire to believe that this is a syntactic and not > semantic feature is a strong one, and given that many developers education > about how the language works is limited to "what does IntelliJ suggest to > me", may not even _realize_ they are giving into the dark side. > > I think it is worth working through the example here for "how would we > recommend handling the case of a "active" row like this. > > I think it's a perfect place for static analysis tooling. One may invent > an annotation like `@NonUpdatable` > with the `RECORD_COMPONENT` target and use it on such fields, then create > an annotation processor > (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that > will check the violations and fail the build if there are any. > Adding such a special case to the language specification would be an > overcomplication. > > With best regards, > Tagir Valeev. > > On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz > wrote: > >> The important mental model here is that a reconstruction (`with`) >> expression is "just" a syntactic optimization for: >> >> - destructure with the canonical deconstruction pattern >> - mutate the components >> - reconstruct with the primary constructor >> >> So the root problem here is not the reconstruction expression; if you can >> bork up your application state with a reconstruction expression, you can >> bork it up without one. >> >> Primary constructors can enforce invariants _on_ or _between_ components, >> such as: >> >> record Rational(int num, int denom) { >> Rational { if (denom == 0) throw ... } >> } >> >> or >> >> record Range(int lo, int hi) { >> Range { if (lo > hi) throw... } >> } >> >> What they can't do is express invariants between the record / carrier >> state and "the rest of the system", because they are supposed to be simple >> data carriers, not serialized references to some external system. A >> class that models a database row in this way is complecting entity state >> with an external entity id. By modeling in this way, you have explicitly >> declared that >> >> rec with { dbId++ } >> >> *is explicitly OK* in your system; that the components of the record can >> be freely combined in any way (modulo enforced cross-component >> invariants). And there are systems in which this is fine! But you're >> imagining (correctly) that this modeling technique will be used in systems >> in which this is not fine. >> >> The main challenge here is that developers will be so attracted to the >> syntactic concision that they will willfully ignore the semantic >> inconsistencies they are creating. >> >> >> >> >> On 1/25/2026 1:37 PM, Andy Gegg wrote: >> >> Hello, >> I apologise for coming late to the party here - Records have been of >> limited use to me but Mr Goetz's email on carrier classes is something that >> would be very useful so I've been thinking about the consequences. >> >> Since carrier classes and records are for data, in a database >> application somewhere or other you're going to get database ids in records: >> record MyRec(int dbId, String name,...) >> >> While everything is immutable this is fine but JEP 468 opens up the >> possibility of mutation: >> >> MyRec rec = readDatabase(...); >> rec = rec with {name="...";}; >> writeDatabase(rec); >> >> which is absolutely fine and what an application wants to do. But: >> MyRec rec = readDatabase(...); >> rec = rec with {dbId++;}; >> writeDatabase(rec); >> >> is disastrous. There's no way the canonical constructor invoked from >> 'with' can detect stupidity nor can whatever the database access layer does. >> >> In the old days, the lack of a 'setter' would usually prevent stupid code >> - the above could be achieved, obviously, but the code is devious enough to >> make people stop and think (one hopes). >> >> Here there is nothing to say "do not update this!!!" except code >> comments, JavaDoc and naming conventions. >> >> It's not always obvious which fields may or may not be changed in the >> application. >> >> record MyRec(int dbId, int fatherId,...) >> probably doesn't want >> rec = rec with { fatherId = ... } >> >> but a HR application will need to be able to do: >> >> record MyRec(int dbId, int departmentId, ...); >> ... >> rec = rec with { departmentId = newDept; }; >> >> Clearly, people can always write stupid code (guilty...) and the current >> state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, >> ...);) which is enough to stop people using records here but carrier >> classes will be very tempting and that brings derived creation back to the >> fore. >> >> It's not just database ids which might need restricting from update, e.g. >> timestamps (which are better done in the database layer) and no doubt >> different applications will have their own business case restrictions. >> >> Thank you for your time, >> Andy Gegg >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jan 26 15:25:03 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Jan 2026 10:25:03 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: I don't think adding the conditional deconstruction story (as interesting as it is!) would shed light on this question.? I think the answer lies in "why does the active-row pattern not obey the requirements for being a carrier."? Whether deconstruction is conditional or unconditional, the problem is still that if someone can create records/carriers with ? ? PersonRow r = new PersonRow(rand.nextInt(), "Bob Smith") and then persist them with ? ? database.persist(r); they are bestowing the right to update random rows that were not dispensed by the ORM.? The database API has, by virtue of the fact that PersonRow has a public constructor that accepts an ID, essentially exposed a wider API than it intended to. The answer has always been "don't use carriers/records for this", but the interesting sub-question is (a) how to explain this succinctly to users so they get it and (b) what to tell them to do instead. On 1/26/2026 10:12 AM, Ethan McCue wrote: > My immediate thought (aside from imagining Brian trapped in an eternal > version of that huffalumps and woozles scene from Winnie the Pooh, but > it's all these emails) is that database entities aren't actually good > candidates for "unconditional deconstruction" > > I think this because the act of getting the data from the > db/persistence context is intrinsically fallible *and* attached to > instance behavior; maybe we need to look forward to what the > conditional deconstruction story would be? > > On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: > > > >> It's interesting that when language designers make the code >> easier to write, somebody may complain that it's too easy :-) > > I too had that "you can't win" feeling :) > > I would recast the question here as "Can Java developers handle > carrier classes".? Records are restricted enough to keep > developers _mostly_ out of trouble, but the desire to believe that > this is a syntactic and not semantic feature is a strong one, and > given that many developers education about how the language works > is limited to "what does IntelliJ suggest to me", may not even > _realize_ they are giving into the dark side. > > I think it is worth working through the example here for "how > would we recommend handling the case of a "active" row like this. > >> I think it's a perfect place for static analysis tooling. One may >> invent an annotation like `@NonUpdatable` >> with the `RECORD_COMPONENT` target and use it on such fields, >> then create an annotation processor >> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), >> that will check the violations and fail the build if there are any. >> Adding such a special case to the language specification would be >> an overcomplication. >> >> With best regards, >> Tagir Valeev. >> >> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >> wrote: >> >> The important mental model here is that a reconstruction >> (`with`) expression is "just" a syntactic optimization for: >> >> ?- destructure with the canonical deconstruction pattern >> ?- mutate the components >> ?- reconstruct with the primary constructor >> >> So the root problem here is not the reconstruction >> expression; if you can bork up your application state with a >> reconstruction expression, you can bork it up without one. >> >> Primary constructors can enforce invariants _on_ or _between_ >> components, such as: >> >> ? ? record Rational(int num, int denom) { >> ? ? ? ? Rational { if (denom == 0) throw ... } >> ? ? } >> >> or >> >> ? ? record Range(int lo, int hi) { >> ? ? ? ? Range { if (lo > hi) throw... } >> ? ? } >> >> What they can't do is express invariants between the record / >> carrier state and "the rest of the system", because they are >> supposed to be simple data carriers, not serialized >> references to some external system. A class that models a >> database row in this way is complecting entity state with an >> external entity id.? By modeling in this way, you have >> explicitly declared that >> >> ? ? rec with { dbId++ } >> >> *is explicitly OK* in your system; that the components of the >> record can be freely combined in any way (modulo enforced >> cross-component invariants).? And there are systems in which >> this is fine!? But you're imagining (correctly) that this >> modeling technique will be used in systems in which this is >> not fine. >> >> The main challenge here is that developers will be so >> attracted to the syntactic concision that they will willfully >> ignore the semantic inconsistencies they are creating. >> >> >> >> >> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>> Hello, >>> I apologise for coming late to the party here - Records have >>> been of limited use to me but Mr Goetz's email on carrier >>> classes is something that would be very useful so I've been >>> thinking about the consequences. >>> >>> Since? carrier classes and records are for data, in a >>> database application somewhere or other you're going to get >>> database ids in records: >>> record MyRec(int dbId, String name,...) >>> >>> While everything is immutable this is fine but JEP 468 opens >>> up the possibility of mutation: >>> >>> MyRec rec?= readDatabase(...); >>> rec = rec with {name="...";}; >>> writeDatabase(rec); >>> >>> which is absolutely fine and what an application wants to >>> do.? But: >>> MyRec rec = readDatabase(...); >>> rec = rec with {dbId++;}; >>> writeDatabase(rec); >>> >>> is disastrous.? There's no way the canonical constructor >>> invoked from 'with' can detect stupidity nor can whatever >>> the database access layer does. >>> >>> In the old days, the lack of a 'setter' would usually >>> prevent stupid code - the above could be achieved, >>> obviously, but the code is devious enough to make people >>> stop and think (one hopes). >>> >>> Here there is nothing to say "do not update this!!!" except >>> code comments, JavaDoc and naming conventions. >>> >>> It's not always obvious which fields may or may not be >>> changed in the application. >>> >>> record MyRec(int dbId, int fatherId,...) >>> probably doesn't want >>> rec = rec with { fatherId = ... } >>> >>> but a HR application will need to be able to do: >>> >>> record MyRec(int dbId, int departmentId, ...); >>> ... >>> rec = rec with { departmentId = newDept; }; >>> >>> Clearly, people can always write stupid code (guilty...) and >>> the current state of play obviously allows the possibility >>> (rec = new MyRec(rec.dbId++, ...);) which is enough to stop >>> people using records here but carrier classes will be very >>> tempting and that brings derived creation back to the fore. >>> >>> It's not just database ids which might need restricting from >>> update, e.g. timestamps (which are better done in the >>> database layer) and no doubt different applications will >>> have their own business case restrictions. >>> >>> Thank you for your time, >>> Andy Gegg >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Mon Jan 26 15:50:35 2026 From: ethan at mccue.dev (Ethan McCue) Date: Mon, 26 Jan 2026 10:50:35 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: When people start marking up their entities with Jackson annotations the explanation I give is basically 1. Entities are a bad fit for the heuristic based class -> json method of serializing data (because circularity, hydration, etc) 2. Rather than try to make them fit either manually produce json or move the data into a class hierarchy that *is* a good fit (which records are) The underlying issue is that people broadly seem to choose the convenience of not writing a field out again over picking semantically sensible mechanisms. If we conceive of carrier classes as mostly "record-like but..." Then I think what we tell people to do won't have changed. It's really just that there's a new "wrong" thing that can be done that will be tempting to do. On Mon, Jan 26, 2026, 10:25?AM Brian Goetz wrote: > I don't think adding the conditional deconstruction story (as interesting > as it is!) would shed light on this question. I think the answer lies in > "why does the active-row pattern not obey the requirements for being a > carrier." Whether deconstruction is conditional or unconditional, the > problem is still that if someone can create records/carriers with > > PersonRow r = new PersonRow(rand.nextInt(), "Bob Smith") > > and then persist them with > > database.persist(r); > > they are bestowing the right to update random rows that were not dispensed > by the ORM. The database API has, by virtue of the fact that PersonRow has > a public constructor that accepts an ID, essentially exposed a wider API > than it intended to. > > The answer has always been "don't use carriers/records for this", but the > interesting sub-question is (a) how to explain this succinctly to users so > they get it and (b) what to tell them to do instead. > > > On 1/26/2026 10:12 AM, Ethan McCue wrote: > > My immediate thought (aside from imagining Brian trapped in an eternal > version of that huffalumps and woozles scene from Winnie the Pooh, but it's > all these emails) is that database entities aren't actually good candidates > for "unconditional deconstruction" > > I think this because the act of getting the data from the db/persistence > context is intrinsically fallible *and* attached to instance behavior; > maybe we need to look forward to what the conditional deconstruction story > would be? > > On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: > >> >> >> It's interesting that when language designers make the code easier to >> write, somebody may complain that it's too easy :-) >> >> >> I too had that "you can't win" feeling :) >> >> I would recast the question here as "Can Java developers handle carrier >> classes". Records are restricted enough to keep developers _mostly_ out of >> trouble, but the desire to believe that this is a syntactic and not >> semantic feature is a strong one, and given that many developers education >> about how the language works is limited to "what does IntelliJ suggest to >> me", may not even _realize_ they are giving into the dark side. >> >> I think it is worth working through the example here for "how would we >> recommend handling the case of a "active" row like this. >> >> I think it's a perfect place for static analysis tooling. One may invent >> an annotation like `@NonUpdatable` >> with the `RECORD_COMPONENT` target and use it on such fields, then create >> an annotation processor >> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that >> will check the violations and fail the build if there are any. >> Adding such a special case to the language specification would be an >> overcomplication. >> >> With best regards, >> Tagir Valeev. >> >> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >> wrote: >> >>> The important mental model here is that a reconstruction (`with`) >>> expression is "just" a syntactic optimization for: >>> >>> - destructure with the canonical deconstruction pattern >>> - mutate the components >>> - reconstruct with the primary constructor >>> >>> So the root problem here is not the reconstruction expression; if you >>> can bork up your application state with a reconstruction expression, you >>> can bork it up without one. >>> >>> Primary constructors can enforce invariants _on_ or _between_ >>> components, such as: >>> >>> record Rational(int num, int denom) { >>> Rational { if (denom == 0) throw ... } >>> } >>> >>> or >>> >>> record Range(int lo, int hi) { >>> Range { if (lo > hi) throw... } >>> } >>> >>> What they can't do is express invariants between the record / carrier >>> state and "the rest of the system", because they are supposed to be simple >>> data carriers, not serialized references to some external system. A >>> class that models a database row in this way is complecting entity state >>> with an external entity id. By modeling in this way, you have explicitly >>> declared that >>> >>> rec with { dbId++ } >>> >>> *is explicitly OK* in your system; that the components of the record can >>> be freely combined in any way (modulo enforced cross-component >>> invariants). And there are systems in which this is fine! But you're >>> imagining (correctly) that this modeling technique will be used in systems >>> in which this is not fine. >>> >>> The main challenge here is that developers will be so attracted to the >>> syntactic concision that they will willfully ignore the semantic >>> inconsistencies they are creating. >>> >>> >>> >>> >>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>> >>> Hello, >>> I apologise for coming late to the party here - Records have been of >>> limited use to me but Mr Goetz's email on carrier classes is something that >>> would be very useful so I've been thinking about the consequences. >>> >>> Since carrier classes and records are for data, in a database >>> application somewhere or other you're going to get database ids in records: >>> record MyRec(int dbId, String name,...) >>> >>> While everything is immutable this is fine but JEP 468 opens up the >>> possibility of mutation: >>> >>> MyRec rec = readDatabase(...); >>> rec = rec with {name="...";}; >>> writeDatabase(rec); >>> >>> which is absolutely fine and what an application wants to do. But: >>> MyRec rec = readDatabase(...); >>> rec = rec with {dbId++;}; >>> writeDatabase(rec); >>> >>> is disastrous. There's no way the canonical constructor invoked from >>> 'with' can detect stupidity nor can whatever the database access layer does. >>> >>> In the old days, the lack of a 'setter' would usually prevent stupid >>> code - the above could be achieved, obviously, but the code is devious >>> enough to make people stop and think (one hopes). >>> >>> Here there is nothing to say "do not update this!!!" except code >>> comments, JavaDoc and naming conventions. >>> >>> It's not always obvious which fields may or may not be changed in the >>> application. >>> >>> record MyRec(int dbId, int fatherId,...) >>> probably doesn't want >>> rec = rec with { fatherId = ... } >>> >>> but a HR application will need to be able to do: >>> >>> record MyRec(int dbId, int departmentId, ...); >>> ... >>> rec = rec with { departmentId = newDept; }; >>> >>> Clearly, people can always write stupid code (guilty...) and the current >>> state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, >>> ...);) which is enough to stop people using records here but carrier >>> classes will be very tempting and that brings derived creation back to the >>> fore. >>> >>> It's not just database ids which might need restricting from update, >>> e.g. timestamps (which are better done in the database layer) and no doubt >>> different applications will have their own business case restrictions. >>> >>> Thank you for your time, >>> Andy Gegg >>> >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From atonita at proton.me Mon Jan 26 15:52:32 2026 From: atonita at proton.me (Aaryn Tonita) Date: Mon, 26 Jan 2026 15:52:32 +0000 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: This past sprint we had such a case where unconditional deconstruction would have helped with database entities. Basically a user had created a patient twice over quite some time span and operations and graft allocations were associated with both patients but the medical and personal details were most accurate on the newest and the user desire was to merge them. Our deduplication detection didn't trigger because of incompleteness of the old record. However because so many downstream systems depend on the oldest record that was the id to keep. In the database you would just alter the id of the old with cascade, then relink the foreign key constraints of related tables to the new patient, then modify the id back to the old value again with cascade and delete the old. Now JPA doesn't support this approach of altering a primary key... So instead you need to fetch the new and old entity and deconstruct the new entity before deleting it and then reconstruct the old entity with the new data and same old id... Or you reach for the @Query approach instead and after modifying the database you change just the id (and linked relations) of the newer patient representation object. This latter approach is less brittle to future changes. But the original point that Brian made stands: the constructor always allows a nonsense representation. People exploit that in unit tests to create unpersisted entities or relations to other entities that don't exist. Without fetching the entire database all at once you won't really get away from that but I also wouldn't want to. We have more and more places where withers would help (and sad places where a carrier class would have helped but we used a class in place of a record). Sent from [Proton Mail](https://proton.me/mail/home) for Android. -------- Original Message -------- On Monday, 01/26/26 at 16:13 Ethan McCue wrote: > My immediate thought (aside from imagining Brian trapped in an eternal version of that huffalumps and woozles scene from Winnie the Pooh, but it's all these emails) is that database entities aren't actually good candidates for "unconditional deconstruction" > > I think this because the act of getting the data from the db/persistence context is intrinsically fallible *and* attached to instance behavior; maybe we need to look forward to what the conditional deconstruction story would be? > > On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: > >>> It's interesting that when language designers make the code easier to write, somebody may complain that it's too easy :-) >> >> I too had that "you can't win" feeling :) >> >> I would recast the question here as "Can Java developers handle carrier classes". Records are restricted enough to keep developers _mostly_ out of trouble, but the desire to believe that this is a syntactic and not semantic feature is a strong one, and given that many developers education about how the language works is limited to "what does IntelliJ suggest to me", may not even _realize_ they are giving into the dark side. >> >> I think it is worth working through the example here for "how would we recommend handling the case of a "active" row like this. >> >>> I think it's a perfect place for static analysis tooling. One may invent an annotation like `@NonUpdatable` >>> with the `RECORD_COMPONENT` target and use it on such fields, then create an annotation processor >>> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that will check the violations and fail the build if there are any. >>> Adding such a special case to the language specification would be an overcomplication. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz wrote: >>> >>>> The important mental model here is that a reconstruction (`with`) expression is "just" a syntactic optimization for: >>>> >>>> - destructure with the canonical deconstruction pattern >>>> - mutate the components >>>> - reconstruct with the primary constructor >>>> >>>> So the root problem here is not the reconstruction expression; if you can bork up your application state with a reconstruction expression, you can bork it up without one. >>>> >>>> Primary constructors can enforce invariants _on_ or _between_ components, such as: >>>> >>>> record Rational(int num, int denom) { >>>> Rational { if (denom == 0) throw ... } >>>> } >>>> >>>> or >>>> >>>> record Range(int lo, int hi) { >>>> Range { if (lo > hi) throw... } >>>> } >>>> >>>> What they can't do is express invariants between the record / carrier state and "the rest of the system", because they are supposed to be simple data carriers, not serialized references to some external system. A class that models a database row in this way is complecting entity state with an external entity id. By modeling in this way, you have explicitly declared that >>>> >>>> rec with { dbId++ } >>>> >>>> *is explicitly OK* in your system; that the components of the record can be freely combined in any way (modulo enforced cross-component invariants). And there are systems in which this is fine! But you're imagining (correctly) that this modeling technique will be used in systems in which this is not fine. >>>> The main challenge here is that developers will be so attracted to the syntactic concision that they will willfully ignore the semantic inconsistencies they are creating. >>>> >>>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>>> >>>>> Hello, >>>>> I apologise for coming late to the party here - Records have been of limited use to me but Mr Goetz's email on carrier classes is something that would be very useful so I've been thinking about the consequences. >>>>> >>>>> Since carrier classes and records are for data, in a database application somewhere or other you're going to get database ids in records: >>>>> record MyRec(int dbId, String name,...) >>>>> >>>>> While everything is immutable this is fine but JEP 468 opens up the possibility of mutation: >>>>> >>>>> MyRec rec = readDatabase(...); >>>>> rec = rec with {name="...";}; >>>>> writeDatabase(rec); >>>>> >>>>> which is absolutely fine and what an application wants to do. But:MyRec rec = readDatabase(...); >>>>> rec = rec with {dbId++;}; >>>>> writeDatabase(rec); >>>>> >>>>> is disastrous. There's no way the canonical constructor invoked from 'with' can detect stupidity nor can whatever the database access layer does. >>>>> >>>>> In the old days, the lack of a 'setter' would usually prevent stupid code - the above could be achieved, obviously, but the code is devious enough to make people stop and think (one hopes). >>>>> >>>>> Here there is nothing to say "do not update this!!!" except code comments, JavaDoc and naming conventions. >>>>> >>>>> It's not always obvious which fields may or may not be changed in the application. >>>>> >>>>> record MyRec(int dbId, int fatherId,...) >>>>> probably doesn't want >>>>> rec = rec with { fatherId = ... } >>>>> >>>>> but a HR application will need to be able to do: >>>>> >>>>> record MyRec(int dbId, int departmentId, ...); >>>>> ... >>>>> rec = rec with { departmentId = newDept; }; >>>>> >>>>> Clearly, people can always write stupid code (guilty...) and the current state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, ...);) which is enough to stop people using records here but carrier classes will be very tempting and that brings derived creation back to the fore. >>>>> >>>>> It's not just database ids which might need restricting from update, e.g. timestamps (which are better done in the database layer) and no doubt different applications will have their own business case restrictions. >>>>> >>>>> Thank you for your time, >>>>> Andy Gegg -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jan 26 15:53:21 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Jan 2026 10:53:21 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: > If we conceive of carrier classes as mostly "record-like but..." Then > I think what we tell people to do won't have changed. It's really just > that there's a new "wrong" thing that can be done that will be > tempting to do. The semantic description is that a carrier class / component class / data class (shed to be painted later) has a complete, canonical, nominal description of its state.? IOW, that the carrier _is_ the data, just like records -- its just you get more flexibility in representation, aka "more rope". The question of this thread is, "is this too much rope". From brian.goetz at oracle.com Mon Jan 26 15:54:47 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Jan 2026 10:54:47 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> I think the entity-modeling story here is more like: ?- a regular class that associates (id, PersonInfo), where the ids are dispensed exclusively by the ORM, and ?- a record/carrier for PersonInfo, that lets you "mutate" the information but not the ID association. On 1/26/2026 10:52 AM, Aaryn Tonita wrote: > This past sprint we had such a case where unconditional deconstruction > would have helped with database entities. Basically a user had created > a patient twice over quite some time span and operations and graft > allocations?were associated with both patients but the medical and > personal details were most accurate on the newest and the user?desire > was to merge them. Our deduplication detection didn't trigger because > of incompleteness of the old record.?However because so many > downstream systems depend on the oldest record that was the id?to keep. > > In the database you would just alter the id of the old with cascade, > then relink the foreign key constraints of related tables to the new > patient, then modify the id back to the old value again with cascade > and delete the old. Now JPA doesn't support this approach of altering > a primary key... So instead you need to fetch the new and old entity > and deconstruct the new entity before deleting it and then reconstruct > the old entity with the new data and same?old id... Or you reach for > the @Query approach instead and after modifying the database you > change just the id (and linked relations) of the newer patient > representation object. This latter approach is less brittle to future > changes. > > But the original point that Brian made stands: the constructor always > allows a nonsense representation. People exploit that in unit tests to > create unpersisted?entities or?relations to other entities that don't > exist. Without fetching the entire database all at once you won't > really get away from that but I also wouldn't want to. > > We have more and more places where withers would help (and sad places > where a carrier class would have helped but we used a class in place > of a record). > > > > > Sent from Proton Mail > > for Android. > > > > -------- Original Message -------- > On Monday, 01/26/26 at 16:13 Ethan McCue wrote: > > My immediate thought (aside from imagining Brian trapped in an > eternal version of that huffalumps and woozles scene from Winnie > the Pooh, but it's all these emails) is that database entities > aren't actually good candidates for "unconditional deconstruction" > > I think this because the act of getting the data from the > db/persistence context is intrinsically fallible *and* attached to > instance behavior; maybe we need to look forward to what the > conditional deconstruction story would be? > > On Mon, Jan 26, 2026, 10:04?AM Brian Goetz > wrote: > > > >> It's interesting that when language designers make the code >> easier to write, somebody may complain that it's too easy :-) > > I too had that "you can't win" feeling :) > > I would recast the question here as "Can Java developers > handle carrier classes".? Records are restricted enough to > keep developers _mostly_ out of trouble, but the desire to > believe that this is a syntactic and not semantic feature is a > strong one, and given that many developers education about how > the language works is limited to "what does IntelliJ suggest > to me", may not even _realize_ they are giving into the dark > side. > > I think it is worth working through the example here for "how > would we recommend handling the case of a "active" row like this. > >> I think it's a perfect place for static analysis tooling. One >> may invent an annotation like `@NonUpdatable` >> with the `RECORD_COMPONENT` target and use it on such fields, >> then create an annotation processor >> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, >> etc.), that will check the violations and fail the build if >> there are any. >> Adding such a special case to the language specification >> would be an overcomplication. >> >> With best regards, >> Tagir Valeev. >> >> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >> wrote: >> >> The important mental model here is that a reconstruction >> (`with`) expression is "just" a syntactic optimization for: >> >> ?- destructure with the canonical deconstruction pattern >> ?- mutate the components >> ?- reconstruct with the primary constructor >> >> So the root problem here is not the reconstruction >> expression; if you can bork up your application state >> with a reconstruction expression, you can bork it up >> without one. >> >> Primary constructors can enforce invariants _on_ or >> _between_ components, such as: >> >> ? ? record Rational(int num, int denom) { >> ? ? ? ? Rational { if (denom == 0) throw ... } >> ? ? } >> >> or >> >> ? ? record Range(int lo, int hi) { >> ? ? ? ? Range { if (lo > hi) throw... } >> ? ? } >> >> What they can't do is express invariants between the >> record / carrier state and "the rest of the system", >> because they are supposed to be simple data carriers, not >> serialized references to some external system. A class >> that models a database row in this way is complecting >> entity state with an external entity id. By modeling in >> this way, you have explicitly declared that >> >> ? ? rec with { dbId++ } >> >> *is explicitly OK* in your system; that the components of >> the record can be freely combined in any way (modulo >> enforced cross-component invariants).? And there are >> systems in which this is fine!? But you're imagining >> (correctly) that this modeling technique will be used in >> systems in which this is not fine. >> >> The main challenge here is that developers will be so >> attracted to the syntactic concision that they will >> willfully ignore the semantic inconsistencies they are >> creating. >> >> >> >> >> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>> Hello, >>> I apologise for coming late to the party here - Records >>> have been of limited use to me but Mr Goetz's email on >>> carrier classes is something that would be very useful >>> so I've been thinking about the consequences. >>> >>> Since? carrier classes and records are for data, in a >>> database application somewhere or other you're going to >>> get database ids in records: >>> record MyRec(int dbId, String name,...) >>> >>> While everything is immutable this is fine but JEP 468 >>> opens up the possibility of mutation: >>> >>> MyRec rec?= readDatabase(...); >>> rec = rec with {name="...";}; >>> writeDatabase(rec); >>> >>> which is absolutely fine and what an application wants >>> to do.? But: >>> MyRec rec = readDatabase(...); >>> rec = rec with {dbId++;}; >>> writeDatabase(rec); >>> >>> is disastrous.? There's no way the canonical constructor >>> invoked from 'with' can detect stupidity nor can >>> whatever the database access layer does. >>> >>> In the old days, the lack of a 'setter' would usually >>> prevent stupid code - the above could be achieved, >>> obviously, but the code is devious enough to make people >>> stop and think (one hopes). >>> >>> Here there is nothing to say "do not update this!!!" >>> except code comments, JavaDoc and naming conventions. >>> >>> It's not always obvious which fields may or may not be >>> changed in the application. >>> >>> record MyRec(int dbId, int fatherId,...) >>> probably doesn't want >>> rec = rec with { fatherId = ... } >>> >>> but a HR application will need to be able to do: >>> >>> record MyRec(int dbId, int departmentId, ...); >>> ... >>> rec = rec with { departmentId = newDept; }; >>> >>> Clearly, people can always write stupid code (guilty...) >>> and the current state of play obviously allows the >>> possibility (rec = new MyRec(rec.dbId++, ...);) which is >>> enough to stop people using records here but carrier >>> classes will be very tempting and that brings derived >>> creation back to the fore. >>> >>> It's not just database ids which might need restricting >>> from update, e.g. timestamps (which are better done in >>> the database layer) and no doubt different applications >>> will have their own business case restrictions. >>> >>> Thank you for your time, >>> Andy Gegg >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Mon Jan 26 16:05:28 2026 From: ethan at mccue.dev (Ethan McCue) Date: Mon, 26 Jan 2026 11:05:28 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> Message-ID: Forgive me if I'm mixing up terms here, but I think a database entity when fetched from some persistence context can actually be a runtime generated subclass that also maintains a reference to the context that produced it. So you'd have an instance of Person and be tempted to write p = p with { name = "..."; }. This could maybe work if the subclass could do its own thing, but if you are feeding the data back into new Person(...) it can't. On Mon, Jan 26, 2026, 10:55?AM Brian Goetz wrote: > I think the entity-modeling story here is more like: > > - a regular class that associates (id, PersonInfo), where the ids are > dispensed exclusively by the ORM, and > - a record/carrier for PersonInfo, that lets you "mutate" the information > but not the ID association. > > > > On 1/26/2026 10:52 AM, Aaryn Tonita wrote: > > This past sprint we had such a case where unconditional deconstruction > would have helped with database entities. Basically a user had created a > patient twice over quite some time span and operations and graft > allocations were associated with both patients but the medical and personal > details were most accurate on the newest and the user desire was to merge > them. Our deduplication detection didn't trigger because of incompleteness > of the old record. However because so many downstream systems depend on the > oldest record that was the id to keep. > > In the database you would just alter the id of the old with cascade, then > relink the foreign key constraints of related tables to the new patient, > then modify the id back to the old value again with cascade and delete the > old. Now JPA doesn't support this approach of altering a primary key... So > instead you need to fetch the new and old entity and deconstruct the new > entity before deleting it and then reconstruct the old entity with the new > data and same old id... Or you reach for the @Query approach instead and > after modifying the database you change just the id (and linked relations) > of the newer patient representation object. This latter approach is less > brittle to future changes. > > But the original point that Brian made stands: the constructor always > allows a nonsense representation. People exploit that in unit tests to > create unpersisted entities or relations to other entities that don't > exist. Without fetching the entire database all at once you won't really > get away from that but I also wouldn't want to. > > We have more and more places where withers would help (and sad places > where a carrier class would have helped but we used a class in place of a > record). > > > > > Sent from Proton Mail > > for Android. > > > > -------- Original Message -------- > On Monday, 01/26/26 at 16:13 Ethan McCue > wrote: > > My immediate thought (aside from imagining Brian trapped in an eternal > version of that huffalumps and woozles scene from Winnie the Pooh, but it's > all these emails) is that database entities aren't actually good candidates > for "unconditional deconstruction" > > I think this because the act of getting the data from the db/persistence > context is intrinsically fallible *and* attached to instance behavior; > maybe we need to look forward to what the conditional deconstruction story > would be? > > On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: > >> >> >> It's interesting that when language designers make the code easier to >> write, somebody may complain that it's too easy :-) >> >> >> I too had that "you can't win" feeling :) >> >> I would recast the question here as "Can Java developers handle carrier >> classes". Records are restricted enough to keep developers _mostly_ out of >> trouble, but the desire to believe that this is a syntactic and not >> semantic feature is a strong one, and given that many developers education >> about how the language works is limited to "what does IntelliJ suggest to >> me", may not even _realize_ they are giving into the dark side. >> >> I think it is worth working through the example here for "how would we >> recommend handling the case of a "active" row like this. >> >> I think it's a perfect place for static analysis tooling. One may invent >> an annotation like `@NonUpdatable` >> with the `RECORD_COMPONENT` target and use it on such fields, then create >> an annotation processor >> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that >> will check the violations and fail the build if there are any. >> Adding such a special case to the language specification would be an >> overcomplication. >> >> With best regards, >> Tagir Valeev. >> >> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >> wrote: >> >>> The important mental model here is that a reconstruction (`with`) >>> expression is "just" a syntactic optimization for: >>> >>> - destructure with the canonical deconstruction pattern >>> - mutate the components >>> - reconstruct with the primary constructor >>> >>> So the root problem here is not the reconstruction expression; if you >>> can bork up your application state with a reconstruction expression, you >>> can bork it up without one. >>> >>> Primary constructors can enforce invariants _on_ or _between_ >>> components, such as: >>> >>> record Rational(int num, int denom) { >>> Rational { if (denom == 0) throw ... } >>> } >>> >>> or >>> >>> record Range(int lo, int hi) { >>> Range { if (lo > hi) throw... } >>> } >>> >>> What they can't do is express invariants between the record / carrier >>> state and "the rest of the system", because they are supposed to be simple >>> data carriers, not serialized references to some external system. A >>> class that models a database row in this way is complecting entity state >>> with an external entity id. By modeling in this way, you have explicitly >>> declared that >>> >>> rec with { dbId++ } >>> >>> *is explicitly OK* in your system; that the components of the record can >>> be freely combined in any way (modulo enforced cross-component >>> invariants). And there are systems in which this is fine! But you're >>> imagining (correctly) that this modeling technique will be used in systems >>> in which this is not fine. >>> >>> The main challenge here is that developers will be so attracted to the >>> syntactic concision that they will willfully ignore the semantic >>> inconsistencies they are creating. >>> >>> >>> >>> >>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>> >>> Hello, >>> I apologise for coming late to the party here - Records have been of >>> limited use to me but Mr Goetz's email on carrier classes is something that >>> would be very useful so I've been thinking about the consequences. >>> >>> Since carrier classes and records are for data, in a database >>> application somewhere or other you're going to get database ids in records: >>> record MyRec(int dbId, String name,...) >>> >>> While everything is immutable this is fine but JEP 468 opens up the >>> possibility of mutation: >>> >>> MyRec rec = readDatabase(...); >>> rec = rec with {name="...";}; >>> writeDatabase(rec); >>> >>> which is absolutely fine and what an application wants to do. But: >>> MyRec rec = readDatabase(...); >>> rec = rec with {dbId++;}; >>> writeDatabase(rec); >>> >>> is disastrous. There's no way the canonical constructor invoked from >>> 'with' can detect stupidity nor can whatever the database access layer does. >>> >>> In the old days, the lack of a 'setter' would usually prevent stupid >>> code - the above could be achieved, obviously, but the code is devious >>> enough to make people stop and think (one hopes). >>> >>> Here there is nothing to say "do not update this!!!" except code >>> comments, JavaDoc and naming conventions. >>> >>> It's not always obvious which fields may or may not be changed in the >>> application. >>> >>> record MyRec(int dbId, int fatherId,...) >>> probably doesn't want >>> rec = rec with { fatherId = ... } >>> >>> but a HR application will need to be able to do: >>> >>> record MyRec(int dbId, int departmentId, ...); >>> ... >>> rec = rec with { departmentId = newDept; }; >>> >>> Clearly, people can always write stupid code (guilty...) and the current >>> state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, >>> ...);) which is enough to stop people using records here but carrier >>> classes will be very tempting and that brings derived creation back to the >>> fore. >>> >>> It's not just database ids which might need restricting from update, >>> e.g. timestamps (which are better done in the database layer) and no doubt >>> different applications will have their own business case restrictions. >>> >>> Thank you for your time, >>> Andy Gegg >>> >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jan 26 16:15:00 2026 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 26 Jan 2026 11:15:00 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> Message-ID: <45b7dd5d-d9bc-4aaa-a804-89b229f9892b@oracle.com> I think we might be saying the same thing.? When I say "regular class", it doesn't matter whether it is hand written or generated; the key is that the right to choose IDs is reserved by that class.? Maybe its hidden behind an interface, maybe not.? But you have an _entity_ class that associates (id, PersonData) and a record/carrier for PersonData, which is _just_ the data. On 1/26/2026 11:05 AM, Ethan McCue wrote: > Forgive me if I'm mixing up terms here, but I think a database entity > when fetched from some persistence context can actually be a runtime > generated subclass that also maintains a reference to the context that > produced it. > > So you'd have an instance of Person and be tempted to write p = p with > { name = "..."; }. This could maybe work if the subclass could do its > own thing, but if you are feeding the data back into new Person(...) > it can't. > > > > On Mon, Jan 26, 2026, 10:55?AM Brian Goetz wrote: > > I think the entity-modeling story here is more like: > > ?- a regular class that associates (id, PersonInfo), where the ids > are dispensed exclusively by the ORM, and > ?- a record/carrier for PersonInfo, that lets you "mutate" the > information but not the ID association. > > > > On 1/26/2026 10:52 AM, Aaryn Tonita wrote: >> This past sprint we had such a case where unconditional >> deconstruction would have helped with database entities. >> Basically a user had created a patient twice over quite some time >> span and operations and graft allocations?were associated with >> both patients but the medical and personal details were most >> accurate on the newest and the user?desire was to merge them. Our >> deduplication detection didn't trigger because of incompleteness >> of the old record.?However because so many downstream systems >> depend on the oldest record that was the id?to keep. >> >> In the database you would just alter the id of the old with >> cascade, then relink the foreign key constraints of related >> tables to the new patient, then modify the id back to the old >> value again with cascade and delete the old. Now JPA doesn't >> support this approach of altering a primary key... So instead you >> need to fetch the new and old entity and deconstruct the new >> entity before deleting it and then reconstruct the old entity >> with the new data and same?old id... Or you reach for the @Query >> approach instead and after modifying the database you change just >> the id (and linked relations) of the newer patient representation >> object. This latter approach is less brittle to future changes. >> >> But the original point that Brian made stands: the constructor >> always allows a nonsense representation. People exploit that in >> unit tests to create unpersisted?entities or?relations to other >> entities that don't exist. Without fetching the entire database >> all at once you won't really get away from that but I also >> wouldn't want to. >> >> We have more and more places where withers would help (and sad >> places where a carrier class would have helped but we used a >> class in place of a record). >> >> >> >> >> Sent from Proton Mail >> >> for Android. >> >> >> >> -------- Original Message -------- >> On Monday, 01/26/26 at 16:13 Ethan McCue >> wrote: >> >> My immediate thought (aside from imagining Brian trapped in >> an eternal version of that huffalumps and woozles scene from >> Winnie the Pooh, but it's all these emails) is that database >> entities aren't actually good candidates for "unconditional >> deconstruction" >> >> I think this because the act of getting the data from the >> db/persistence context is intrinsically fallible *and* >> attached to instance behavior; maybe we need to look forward >> to what the conditional deconstruction story would be? >> >> On Mon, Jan 26, 2026, 10:04?AM Brian Goetz >> wrote: >> >> >> >>> It's interesting that when language designers make the >>> code easier to write, somebody may complain that it's >>> too easy :-) >> >> I too had that "you can't win" feeling :) >> >> I would recast the question here as "Can Java developers >> handle carrier classes". Records are restricted enough to >> keep developers _mostly_ out of trouble, but the desire >> to believe that this is a syntactic and not semantic >> feature is a strong one, and given that many developers >> education about how the language works is limited to >> "what does IntelliJ suggest to me", may not even >> _realize_ they are giving into the dark side. >> >> I think it is worth working through the example here for >> "how would we recommend handling the case of a "active" >> row like this. >> >>> I think it's a perfect place for static analysis >>> tooling. One may invent an annotation like `@NonUpdatable` >>> with the `RECORD_COMPONENT` target and use it on such >>> fields, then create an annotation processor >>> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL >>> rule, etc.), that will check the violations and fail the >>> build if there are any. >>> Adding such a special case to the language specification >>> would be an overcomplication. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >>> wrote: >>> >>> The important mental model here is that a >>> reconstruction (`with`) expression is "just" a >>> syntactic optimization for: >>> >>> ?- destructure with the canonical deconstruction pattern >>> ?- mutate the components >>> ?- reconstruct with the primary constructor >>> >>> So the root problem here is not the reconstruction >>> expression; if you can bork up your application >>> state with a reconstruction expression, you can bork >>> it up without one. >>> >>> Primary constructors can enforce invariants _on_ or >>> _between_ components, such as: >>> >>> ? ? record Rational(int num, int denom) { >>> ? ? ? ? Rational { if (denom == 0) throw ... } >>> ? ? } >>> >>> or >>> >>> ? ? record Range(int lo, int hi) { >>> ? ? ? ? Range { if (lo > hi) throw... } >>> ? ? } >>> >>> What they can't do is express invariants between the >>> record / carrier state and "the rest of the system", >>> because they are supposed to be simple data >>> carriers, not serialized references to some external >>> system. A class that models a database row in this >>> way is complecting entity state with an external >>> entity id.? By modeling in this way, you have >>> explicitly declared that >>> >>> ? ? rec with { dbId++ } >>> >>> *is explicitly OK* in your system; that the >>> components of the record can be freely combined in >>> any way (modulo enforced cross-component >>> invariants).? And there are systems in which this is >>> fine! But you're imagining (correctly) that this >>> modeling technique will be used in systems in which >>> this is not fine. >>> >>> The main challenge here is that developers will be >>> so attracted to the syntactic concision that they >>> will willfully ignore the semantic inconsistencies >>> they are creating. >>> >>> >>> >>> >>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>>> Hello, >>>> I apologise for coming late to the party here - >>>> Records have been of limited use to me but Mr >>>> Goetz's email on carrier classes is something that >>>> would be very useful so I've been thinking about >>>> the consequences. >>>> >>>> Since? carrier classes and records are for data, in >>>> a database application somewhere or other you're >>>> going to get database ids in records: >>>> record MyRec(int dbId, String name,...) >>>> >>>> While everything is immutable this is fine but JEP >>>> 468 opens up the possibility of mutation: >>>> >>>> MyRec rec?= readDatabase(...); >>>> rec = rec with {name="...";}; >>>> writeDatabase(rec); >>>> >>>> which is absolutely fine and what an application >>>> wants to do.? But: >>>> MyRec rec = readDatabase(...); >>>> rec = rec with {dbId++;}; >>>> writeDatabase(rec); >>>> >>>> is disastrous.? There's no way the canonical >>>> constructor invoked from 'with' can detect >>>> stupidity nor can whatever the database access >>>> layer does. >>>> >>>> In the old days, the lack of a 'setter' would >>>> usually prevent stupid code - the above could be >>>> achieved, obviously, but the code is devious enough >>>> to make people stop and think (one hopes). >>>> >>>> Here there is nothing to say "do not update >>>> this!!!" except code comments, JavaDoc and naming >>>> conventions. >>>> >>>> It's not always obvious which fields may or may not >>>> be changed in the application. >>>> >>>> record MyRec(int dbId, int fatherId,...) >>>> probably doesn't want >>>> rec = rec with { fatherId = ... } >>>> >>>> but a HR application will need to be able to do: >>>> >>>> record MyRec(int dbId, int departmentId, ...); >>>> ... >>>> rec = rec with { departmentId = newDept; }; >>>> >>>> Clearly, people can always write stupid code >>>> (guilty...) and the current state of play obviously >>>> allows the possibility (rec = new MyRec(rec.dbId++, >>>> ...);) which is enough to stop people using records >>>> here but carrier classes will be very tempting and >>>> that brings derived creation back to the fore. >>>> >>>> It's not just database ids which might need >>>> restricting from update, e.g. timestamps (which are >>>> better done in the database layer) and no doubt >>>> different applications will have their own business >>>> case restrictions. >>>> >>>> Thank you for your time, >>>> Andy Gegg >>>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Mon Jan 26 16:18:44 2026 From: ethan at mccue.dev (Ethan McCue) Date: Mon, 26 Jan 2026 11:18:44 -0500 Subject: JEP 468 updating non-updatable fields In-Reply-To: <45b7dd5d-d9bc-4aaa-a804-89b229f9892b@oracle.com> References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> <45b7dd5d-d9bc-4aaa-a804-89b229f9892b@oracle.com> Message-ID: I'm alluding to state beyond the right to choose ids, but yes: (, PersonData) On Mon, Jan 26, 2026, 11:15?AM Brian Goetz wrote: > I think we might be saying the same thing. When I say "regular class", it > doesn't matter whether it is hand written or generated; the key is that the > right to choose IDs is reserved by that class. Maybe its hidden behind an > interface, maybe not. But you have an _entity_ class that associates (id, > PersonData) and a record/carrier for PersonData, which is _just_ the data. > > On 1/26/2026 11:05 AM, Ethan McCue wrote: > > Forgive me if I'm mixing up terms here, but I think a database entity when > fetched from some persistence context can actually be a runtime generated > subclass that also maintains a reference to the context that produced it. > > So you'd have an instance of Person and be tempted to write p = p with { > name = "..."; }. This could maybe work if the subclass could do its own > thing, but if you are feeding the data back into new Person(...) it can't. > > > > On Mon, Jan 26, 2026, 10:55?AM Brian Goetz wrote: > >> I think the entity-modeling story here is more like: >> >> - a regular class that associates (id, PersonInfo), where the ids are >> dispensed exclusively by the ORM, and >> - a record/carrier for PersonInfo, that lets you "mutate" the >> information but not the ID association. >> >> >> >> On 1/26/2026 10:52 AM, Aaryn Tonita wrote: >> >> This past sprint we had such a case where unconditional deconstruction >> would have helped with database entities. Basically a user had created a >> patient twice over quite some time span and operations and graft >> allocations were associated with both patients but the medical and personal >> details were most accurate on the newest and the user desire was to merge >> them. Our deduplication detection didn't trigger because of incompleteness >> of the old record. However because so many downstream systems depend on the >> oldest record that was the id to keep. >> >> In the database you would just alter the id of the old with cascade, then >> relink the foreign key constraints of related tables to the new patient, >> then modify the id back to the old value again with cascade and delete the >> old. Now JPA doesn't support this approach of altering a primary key... So >> instead you need to fetch the new and old entity and deconstruct the new >> entity before deleting it and then reconstruct the old entity with the new >> data and same old id... Or you reach for the @Query approach instead and >> after modifying the database you change just the id (and linked relations) >> of the newer patient representation object. This latter approach is less >> brittle to future changes. >> >> But the original point that Brian made stands: the constructor always >> allows a nonsense representation. People exploit that in unit tests to >> create unpersisted entities or relations to other entities that don't >> exist. Without fetching the entire database all at once you won't really >> get away from that but I also wouldn't want to. >> >> We have more and more places where withers would help (and sad places >> where a carrier class would have helped but we used a class in place of a >> record). >> >> >> >> >> Sent from Proton Mail >> >> for Android. >> >> >> >> -------- Original Message -------- >> On Monday, 01/26/26 at 16:13 Ethan McCue >> wrote: >> >> My immediate thought (aside from imagining Brian trapped in an eternal >> version of that huffalumps and woozles scene from Winnie the Pooh, but it's >> all these emails) is that database entities aren't actually good candidates >> for "unconditional deconstruction" >> >> I think this because the act of getting the data from the db/persistence >> context is intrinsically fallible *and* attached to instance behavior; >> maybe we need to look forward to what the conditional deconstruction story >> would be? >> >> On Mon, Jan 26, 2026, 10:04?AM Brian Goetz >> wrote: >> >>> >>> >>> It's interesting that when language designers make the code easier to >>> write, somebody may complain that it's too easy :-) >>> >>> >>> I too had that "you can't win" feeling :) >>> >>> I would recast the question here as "Can Java developers handle carrier >>> classes". Records are restricted enough to keep developers _mostly_ out of >>> trouble, but the desire to believe that this is a syntactic and not >>> semantic feature is a strong one, and given that many developers education >>> about how the language works is limited to "what does IntelliJ suggest to >>> me", may not even _realize_ they are giving into the dark side. >>> >>> I think it is worth working through the example here for "how would we >>> recommend handling the case of a "active" row like this. >>> >>> I think it's a perfect place for static analysis tooling. One may invent >>> an annotation like `@NonUpdatable` >>> with the `RECORD_COMPONENT` target and use it on such fields, then >>> create an annotation processor >>> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that >>> will check the violations and fail the build if there are any. >>> Adding such a special case to the language specification would be an >>> overcomplication. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz >>> wrote: >>> >>>> The important mental model here is that a reconstruction (`with`) >>>> expression is "just" a syntactic optimization for: >>>> >>>> - destructure with the canonical deconstruction pattern >>>> - mutate the components >>>> - reconstruct with the primary constructor >>>> >>>> So the root problem here is not the reconstruction expression; if you >>>> can bork up your application state with a reconstruction expression, you >>>> can bork it up without one. >>>> >>>> Primary constructors can enforce invariants _on_ or _between_ >>>> components, such as: >>>> >>>> record Rational(int num, int denom) { >>>> Rational { if (denom == 0) throw ... } >>>> } >>>> >>>> or >>>> >>>> record Range(int lo, int hi) { >>>> Range { if (lo > hi) throw... } >>>> } >>>> >>>> What they can't do is express invariants between the record / carrier >>>> state and "the rest of the system", because they are supposed to be simple >>>> data carriers, not serialized references to some external system. A >>>> class that models a database row in this way is complecting entity state >>>> with an external entity id. By modeling in this way, you have explicitly >>>> declared that >>>> >>>> rec with { dbId++ } >>>> >>>> *is explicitly OK* in your system; that the components of the record >>>> can be freely combined in any way (modulo enforced cross-component >>>> invariants). And there are systems in which this is fine! But you're >>>> imagining (correctly) that this modeling technique will be used in systems >>>> in which this is not fine. >>>> >>>> The main challenge here is that developers will be so attracted to the >>>> syntactic concision that they will willfully ignore the semantic >>>> inconsistencies they are creating. >>>> >>>> >>>> >>>> >>>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>>> >>>> Hello, >>>> I apologise for coming late to the party here - Records have been of >>>> limited use to me but Mr Goetz's email on carrier classes is something that >>>> would be very useful so I've been thinking about the consequences. >>>> >>>> Since carrier classes and records are for data, in a database >>>> application somewhere or other you're going to get database ids in records: >>>> record MyRec(int dbId, String name,...) >>>> >>>> While everything is immutable this is fine but JEP 468 opens up the >>>> possibility of mutation: >>>> >>>> MyRec rec = readDatabase(...); >>>> rec = rec with {name="...";}; >>>> writeDatabase(rec); >>>> >>>> which is absolutely fine and what an application wants to do. But: >>>> MyRec rec = readDatabase(...); >>>> rec = rec with {dbId++;}; >>>> writeDatabase(rec); >>>> >>>> is disastrous. There's no way the canonical constructor invoked from >>>> 'with' can detect stupidity nor can whatever the database access layer does. >>>> >>>> In the old days, the lack of a 'setter' would usually prevent stupid >>>> code - the above could be achieved, obviously, but the code is devious >>>> enough to make people stop and think (one hopes). >>>> >>>> Here there is nothing to say "do not update this!!!" except code >>>> comments, JavaDoc and naming conventions. >>>> >>>> It's not always obvious which fields may or may not be changed in the >>>> application. >>>> >>>> record MyRec(int dbId, int fatherId,...) >>>> probably doesn't want >>>> rec = rec with { fatherId = ... } >>>> >>>> but a HR application will need to be able to do: >>>> >>>> record MyRec(int dbId, int departmentId, ...); >>>> ... >>>> rec = rec with { departmentId = newDept; }; >>>> >>>> Clearly, people can always write stupid code (guilty...) and the >>>> current state of play obviously allows the possibility (rec = new >>>> MyRec(rec.dbId++, ...);) which is enough to stop people using records here >>>> but carrier classes will be very tempting and that brings derived creation >>>> back to the fore. >>>> >>>> It's not just database ids which might need restricting from update, >>>> e.g. timestamps (which are better done in the database layer) and no doubt >>>> different applications will have their own business case restrictions. >>>> >>>> Thank you for your time, >>>> Andy Gegg >>>> >>>> >>>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Mon Jan 26 17:02:42 2026 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Mon, 26 Jan 2026 19:02:42 +0200 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: It seems like the problem is the flexibility combined with the ease of use. Fundamentally, if you represent *mutation* using records you are bound to have mismatches between different levels of the application. Trying to mutate a record in a db using Java's record is bound to create problems. Similarly, trying to respect db record (whose identity is a *specific column/list of columns*) as data-as-identity constructs (record/carriers) will also have mismatches. No amount of language level features will solve this. To bridge the gap you would need to wrap the API with some construct that make the identity immutable. Until now the problem is not too bad because to "make a stupid mistake" takes effort, but with Withers it becomes easy. To solve this problem without introducing new languages level type (adding language level annotation to specific fields is something I would call a new language level type) we would need to do something like "wither-validator": record DbRecord(long id, String f0, String f1, ...) { withCheck(DbRecord newValue) { if (newValue.id() != this.id()) throw ... } } Which the JVM calls automatically after the wither expression ends but before it returns the value. So myRecord with { id+1 } Will throw runtime exception. I'm not sure how worth this feature is. Holo The Wise Wolf Of Yoitsu On Mon, 26 Jan 2026, 18:26 Brian Goetz, wrote: > > > If we conceive of carrier classes as mostly "record-like but..." Then > > I think what we tell people to do won't have changed. It's really just > > that there's a new "wrong" thing that can be done that will be > > tempting to do. > > The semantic description is that a carrier class / component class / > data class (shed to be painted later) has a complete, canonical, nominal > description of its state. IOW, that the carrier _is_ the data, just > like records -- its just you get more flexibility in representation, aka > "more rope". > > The question of this thread is, "is this too much rope". > -------------- next part -------------- An HTML attachment was scrubbed... URL: From atonita at proton.me Mon Jan 26 17:44:50 2026 From: atonita at proton.me (Aaryn Tonita) Date: Mon, 26 Jan 2026 17:44:50 +0000 Subject: JEP 468 updating non-updatable fields In-Reply-To: <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> <83e878ac-c8c9-4287-a1c0-02750937b083@oracle.com> Message-ID: Yes, agreed. What I was hoping to illustrate was that the id (or a class fixing the id) has some precise semantics, relational semantics with and without provenance and that various APIs that already exist expose those in different ways that already leak those semantics. Dealing with those semantics directly isn't necessarily more dangerous than trying to obscure it: the sql in my example being more straightforward than the usual JPA while still safe. Either you store provenance in an id class which only comes from the orm or in an unmodifiable field of a non-data class (which now can only come from the orm) or you let the api user manage provenance. I wouldn't want a record to represent provenace (so the id or the whole object in the second case) because I don't want to expose its constructor and I wouldn't use a carrier for the same reason. JEP 468 doesn't alter this story as far as I can see because if you want true provenance you need to control that behind a private/package protected constructor or some RAII: it isn't data alone, it is history. I also wouldn't really ever want a File or a Socket to be a record or carrier, but 2 out of 3 of these database api approaches lend themself naturally and maybe safely to records and carriers: the first with provenance and the third without. JEP 468 doesn't change this because you already chose the approach when you made the constructirs. On Monday, January 26th, 2026 at 4:55 PM, Brian Goetz wrote: > I think the entity-modeling story here is more like: > > - a regular class that associates (id, PersonInfo), where the ids are dispensed exclusively by the ORM, and > - a record/carrier for PersonInfo, that lets you "mutate" the information but not the ID association. > > On 1/26/2026 10:52 AM, Aaryn Tonita wrote: > >> This past sprint we had such a case where unconditional deconstruction would have helped with database entities. Basically a user had created a patient twice over quite some time span and operations and graft allocations were associated with both patients but the medical and personal details were most accurate on the newest and the user desire was to merge them. Our deduplication detection didn't trigger because of incompleteness of the old record. However because so many downstream systems depend on the oldest record that was the id to keep. >> >> In the database you would just alter the id of the old with cascade, then relink the foreign key constraints of related tables to the new patient, then modify the id back to the old value again with cascade and delete the old. Now JPA doesn't support this approach of altering a primary key... So instead you need to fetch the new and old entity and deconstruct the new entity before deleting it and then reconstruct the old entity with the new data and same old id... Or you reach for the @Query approach instead and after modifying the database you change just the id (and linked relations) of the newer patient representation object. This latter approach is less brittle to future changes. >> >> But the original point that Brian made stands: the constructor always allows a nonsense representation. People exploit that in unit tests to create unpersisted entities or relations to other entities that don't exist. Without fetching the entire database all at once you won't really get away from that but I also wouldn't want to. >> >> We have more and more places where withers would help (and sad places where a carrier class would have helped but we used a class in place of a record). >> >> Sent from [Proton Mail](https://urldefense.com/v3/__https://proton.me/mail/home__;!!ACWV5N9M2RV99hQ!M9c8nZS-3Ga3cPLqwU5QkNrUJs_RoN8etK5ZrHbzmmz29wzkUXoSJmTLdIVhe9GYuWhACJi2C0eIRWF10YI$) for Android. >> >> -------- Original Message -------- >> On Monday, 01/26/26 at 16:13 Ethan McCue [](mailto:ethan at mccue.dev) wrote: >> >>> My immediate thought (aside from imagining Brian trapped in an eternal version of that huffalumps and woozles scene from Winnie the Pooh, but it's all these emails) is that database entities aren't actually good candidates for "unconditional deconstruction" >>> >>> I think this because the act of getting the data from the db/persistence context is intrinsically fallible *and* attached to instance behavior; maybe we need to look forward to what the conditional deconstruction story would be? >>> >>> On Mon, Jan 26, 2026, 10:04?AM Brian Goetz wrote: >>> >>>>> It's interesting that when language designers make the code easier to write, somebody may complain that it's too easy :-) >>>> >>>> I too had that "you can't win" feeling :) >>>> >>>> I would recast the question here as "Can Java developers handle carrier classes". Records are restricted enough to keep developers _mostly_ out of trouble, but the desire to believe that this is a syntactic and not semantic feature is a strong one, and given that many developers education about how the language works is limited to "what does IntelliJ suggest to me", may not even _realize_ they are giving into the dark side. >>>> >>>> I think it is worth working through the example here for "how would we recommend handling the case of a "active" row like this. >>>> >>>>> I think it's a perfect place for static analysis tooling. One may invent an annotation like `@NonUpdatable` >>>>> with the `RECORD_COMPONENT` target and use it on such fields, then create an annotation processor >>>>> (ErrorProne plugin, IntelliJ IDEA inspection, CodeQL rule, etc.), that will check the violations and fail the build if there are any. >>>>> Adding such a special case to the language specification would be an overcomplication. >>>>> >>>>> With best regards, >>>>> Tagir Valeev. >>>>> >>>>> On Sun, Jan 25, 2026 at 11:48?PM Brian Goetz wrote: >>>>> >>>>>> The important mental model here is that a reconstruction (`with`) expression is "just" a syntactic optimization for: >>>>>> >>>>>> - destructure with the canonical deconstruction pattern >>>>>> - mutate the components >>>>>> - reconstruct with the primary constructor >>>>>> >>>>>> So the root problem here is not the reconstruction expression; if you can bork up your application state with a reconstruction expression, you can bork it up without one. >>>>>> >>>>>> Primary constructors can enforce invariants _on_ or _between_ components, such as: >>>>>> >>>>>> record Rational(int num, int denom) { >>>>>> Rational { if (denom == 0) throw ... } >>>>>> } >>>>>> >>>>>> or >>>>>> >>>>>> record Range(int lo, int hi) { >>>>>> Range { if (lo > hi) throw... } >>>>>> } >>>>>> >>>>>> What they can't do is express invariants between the record / carrier state and "the rest of the system", because they are supposed to be simple data carriers, not serialized references to some external system. A class that models a database row in this way is complecting entity state with an external entity id. By modeling in this way, you have explicitly declared that >>>>>> >>>>>> rec with { dbId++ } >>>>>> >>>>>> *is explicitly OK* in your system; that the components of the record can be freely combined in any way (modulo enforced cross-component invariants). And there are systems in which this is fine! But you're imagining (correctly) that this modeling technique will be used in systems in which this is not fine. >>>>>> The main challenge here is that developers will be so attracted to the syntactic concision that they will willfully ignore the semantic inconsistencies they are creating. >>>>>> >>>>>> On 1/25/2026 1:37 PM, Andy Gegg wrote: >>>>>> >>>>>>> Hello, >>>>>>> I apologise for coming late to the party here - Records have been of limited use to me but Mr Goetz's email on carrier classes is something that would be very useful so I've been thinking about the consequences. >>>>>>> >>>>>>> Since carrier classes and records are for data, in a database application somewhere or other you're going to get database ids in records: >>>>>>> record MyRec(int dbId, String name,...) >>>>>>> >>>>>>> While everything is immutable this is fine but JEP 468 opens up the possibility of mutation: >>>>>>> >>>>>>> MyRec rec = readDatabase(...); >>>>>>> rec = rec with {name="...";}; >>>>>>> writeDatabase(rec); >>>>>>> >>>>>>> which is absolutely fine and what an application wants to do. But:MyRec rec = readDatabase(...); >>>>>>> rec = rec with {dbId++;}; >>>>>>> writeDatabase(rec); >>>>>>> >>>>>>> is disastrous. There's no way the canonical constructor invoked from 'with' can detect stupidity nor can whatever the database access layer does. >>>>>>> >>>>>>> In the old days, the lack of a 'setter' would usually prevent stupid code - the above could be achieved, obviously, but the code is devious enough to make people stop and think (one hopes). >>>>>>> >>>>>>> Here there is nothing to say "do not update this!!!" except code comments, JavaDoc and naming conventions. >>>>>>> >>>>>>> It's not always obvious which fields may or may not be changed in the application. >>>>>>> >>>>>>> record MyRec(int dbId, int fatherId,...) >>>>>>> probably doesn't want >>>>>>> rec = rec with { fatherId = ... } >>>>>>> >>>>>>> but a HR application will need to be able to do: >>>>>>> >>>>>>> record MyRec(int dbId, int departmentId, ...); >>>>>>> ... >>>>>>> rec = rec with { departmentId = newDept; }; >>>>>>> >>>>>>> Clearly, people can always write stupid code (guilty...) and the current state of play obviously allows the possibility (rec = new MyRec(rec.dbId++, ...);) which is enough to stop people using records here but carrier classes will be very tempting and that brings derived creation back to the fore. >>>>>>> >>>>>>> It's not just database ids which might need restricting from update, e.g. timestamps (which are better done in the database layer) and no doubt different applications will have their own business case restrictions. >>>>>>> >>>>>>> Thank you for your time, >>>>>>> Andy Gegg -------------- next part -------------- An HTML attachment was scrubbed... URL: From contact at mechite.com Mon Jan 26 19:04:42 2026 From: contact at mechite.com (Mahied Maruf) Date: Mon, 26 Jan 2026 19:04:42 +0000 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: > Holo The Sage Wolf says: > It seems like the problem is the flexibility combined with the ease of use. > > Fundamentally, if you represent *mutation* using records you are bound to > have mismatches between different levels of the application. I think it's important to understand this semantic distinction properly. You proposed a syntax to prevent derivation of an existing record if the identifier was changed, but to me this seems also like a case that could make sense and enforcement of it should be part of the database layer (ORM etc). Brian proposed that a seperate class (or record) could be used just for the identifier and I both agree with this mental model and am already seeing it being done in e.g. representation of concatenated primary key where it already is much more intuitive to perform validation (among other things) this way. Best regards, Mahied Maruf From briangoetz at openjdk.org Mon Jan 26 19:30:40 2026 From: briangoetz at openjdk.org (Brian Goetz) Date: Mon, 26 Jan 2026 19:30:40 GMT Subject: [amber-docs] Withdrawn: Capitalize article titles according to Chicago style In-Reply-To: References: Message-ID: On Sun, 18 Jan 2026 21:24:19 GMT, Nicolai Parlog wrote: > Capitalize article titles according to Chicago style This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/amber-docs/pull/28 From holo3146 at gmail.com Mon Jan 26 19:39:31 2026 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Mon, 26 Jan 2026 21:39:31 +0200 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> <05243904-65d8-4615-91b6-8313a9f4e300@oracle.com> Message-ID: Maybe I wasn't clear enough. When I said > To bridge the gap you would need to wrap the API with some construct that make the identity immutable. I meant something along the lines of Brian's method. My toy example was to demonstrate another possible solution to how to solve the issue at language level *if we were to decide this is were it should be solved there*. I do not wish to put this idea as a suggestion for a language improvement. Holo The Wise Wolf Of Yoitsu On Mon, 26 Jan 2026, 21:04 Mahied Maruf, wrote: > > Holo The Sage Wolf says: > > It seems like the problem is the flexibility combined with the ease of > use. > > > > Fundamentally, if you represent *mutation* using records you are bound to > > have mismatches between different levels of the application. > > I think it's important to understand this semantic distinction > properly. You proposed a syntax to prevent derivation of an existing > record if the identifier was changed, but to me this seems also like a > case that could make sense and enforcement of it should be part of the > database layer (ORM etc). Brian proposed that a seperate class > (or record) could be used just for the identifier and I both agree > with this mental model and am already seeing it being done in e.g. > representation of concatenated primary key where it already is much > more intuitive to perform validation (among other things) this way. > > Best regards, > Mahied Maruf > -------------- next part -------------- An HTML attachment was scrubbed... URL: From develop4lasu at gmail.com Tue Jan 27 07:34:40 2026 From: develop4lasu at gmail.com (=?UTF-8?Q?Marek_Kozie=C5=82?=) Date: Tue, 27 Jan 2026 08:34:40 +0100 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: To sum up, I fully agree that this is an extremely niche feature. For me it sits just above try-with-resources: not something you use everywhere, but when it fits, it removes a class of problems quite effectively. I?ve tried to point that out explicitly in the post. > Having references to other forums does not improve matters. Two clarifications here: Project Coin was a place for proposing language changes, but in practice most of the "chosen" features were those already prepared and feasible in the time frame. For many other proposals there was only brief, time boxed discussion, and we were explicitly asked to stop sending additional ideas. The fact that something didn?t make it into that one release does not mean it was thoroughly evaluated and rejected on principle. We?ve since seen: Glue classes ? a more general/complex analogue of what later appeared as extension functions in Kotlin (introduced only a couple of years later). "Final interfaces" ? effectively the simplified proposals that Java reintroduced in 2020 as sealed types. So there is precedent for ideas discussed back then being dropped for schedule/complexity reasons, then reappearing later. I reference those discussions to show continuity of thought, not as an argument from authority. Reddit references I included mainly to reduce repeated questions and give readers a place to see prior objections and clarifications, not as proof that the idea is good. I?ve been looking for areas where this idea would be especially useful, and it?s entirely possible that such areas are rare ? or that people are simply not used to focusing on this kind of problem. > I'd kick this back to the author to gain experience in writing with the idioms and the spirit of the language as using block scope is a basic skill. That?s exactly why this concept is here: over the years I?ve found cases where relying only on block scope or refactoring was not ideal in practice. The "classic" example you mention is already in the post: you start with simple code, then later add validation, normalization, or security constraints. But when bug happen then choice often becomes: do a total refactor (new methods, extra scopes, renamed variables, more surface area), or do fix without any safeguard against misusing the old variables - this can mean a difference of days. In real projects, especially under time pressure, people very often choose the second option. "forget" keeps diffs small, preserves history, and still gives a hard safety guarantee - without introducing additional risks. -- Greetings Marek Kozie? ( Lasu ) -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Jan 27 16:14:37 2026 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 27 Jan 2026 17:14:37 +0100 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: The "try-with-resources" is ubiquitous, and makes the code cleaner (and in fact better due to `addSuppressed`). While this feature would end up with a Checkstyle feature developed to disallow this (and I would turn on such a rule). This is because this feature is to help bad code stay bad for longer. The feature has no security benefit (despite you alluding to it), because if people want to add a line that would end up being a security issue, then "forget" will not stop them, because one, they can remove it, second they can place their code before the forget (most likely). They will not skip doing that, because they already thought it out that this is what they want, and "forget" does not imply that anything is insecure after (a comment is way more likely to stop something bad). Marek Kozie? ezt ?rta (id?pont: 2026. jan. 27., K, 8:35): > To sum up, I fully agree that this is an extremely niche feature. For me > it sits just above try-with-resources: not something you use everywhere, > but when it fits, it removes a class of problems quite effectively. I?ve > tried to point that out explicitly in the post. > > > Having references to other forums does not improve matters. > > Two clarifications here: > Project Coin was a place for proposing language changes, but in practice > most of the "chosen" features were those already prepared and feasible in > the time frame. For many other proposals there was only brief, time boxed > discussion, and we were explicitly asked to stop sending additional ideas. > The fact that something didn?t make it into that one release does not mean > it was thoroughly evaluated and rejected on principle. We?ve since seen: > Glue classes ? a more general/complex analogue of what later appeared as > extension functions in Kotlin (introduced only a couple of years later). > "Final interfaces" ? effectively the simplified proposals that Java > reintroduced in 2020 as sealed types. > So there is precedent for ideas discussed back then being dropped for > schedule/complexity reasons, then reappearing later. I reference those > discussions to show continuity of thought, not as an argument from > authority. > > Reddit references I included mainly to reduce repeated questions and give > readers a place to see prior objections and clarifications, not as proof > that the idea is good. > > I?ve been looking for areas where this idea would be especially useful, > and it?s entirely possible that such areas are rare ? or that people are > simply not used to focusing on this kind of problem. > > > > I'd kick this back to the author to gain experience in writing with the > idioms and the spirit of the language as using block scope is a basic skill. > That?s exactly why this concept is here: over the years I?ve found cases > where relying only on block scope or refactoring was not ideal in practice. > > The "classic" example you mention is already in the post: you start with > simple code, then later add validation, normalization, or security > constraints. But when bug happen then choice often becomes: > do a total refactor (new methods, extra scopes, renamed variables, more > surface area), or do fix without any safeguard against misusing the old > variables - this can mean a difference of days. > In real projects, especially under time pressure, people very often choose > the second option. "forget" keeps diffs small, preserves history, and still > gives a hard safety guarantee - without introducing additional risks. > > > > -- > Greetings > Marek Kozie? ( Lasu ) > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Jan 27 16:31:53 2026 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 27 Jan 2026 10:31:53 -0600 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: I feel the need to push back a little bit against the idea-bashing here... I completely agree that adding a new "forget" keyword to the Java language doesn't meet the required cost/benefit standard. However that's a separate criticism from the underlying goal, which I like - i.e., help simplify the mental accounting that developers must juggle in their heads. If I am looking at a method that starts like this: void method() { // Grab a highly sensitive object final NuclearLaunchButton btn = NuclearLaunchButton.instance(); ... // Do other stuff... ... } It would be nice to know as soon as possible when "btn" is no longer going to be used in that method. So this is a perfectly appropriate task for IDE's or static code checkers, e.g.: void method() { // Grab a highly sensitive object final NuclearLaunchButton btn = NuclearLaunchButton.instance(); // Check status final boolean armed = btn.isArmed(); // *@last-use(btn)* // Do other stuff... } -Archie On Tue, Jan 27, 2026 at 10:15?AM Attila Kelemen wrote: > The "try-with-resources" is ubiquitous, and makes the code cleaner (and in > fact better due to `addSuppressed`). While this feature would end up with a > Checkstyle feature developed to disallow this (and I would turn on such a > rule). This is because this feature is to help bad code stay bad for > longer. The feature has no security benefit (despite you alluding to it), > because if people want to add a line that would end up being a security > issue, then "forget" will not stop them, because one, they can remove it, > second they can place their code before the forget (most likely). They will > not skip doing that, because they already thought it out that this is what > they want, and "forget" does not imply that anything is insecure after (a > comment is way more likely to stop something bad). > > Marek Kozie? ezt ?rta (id?pont: 2026. jan. 27., > K, 8:35): > >> To sum up, I fully agree that this is an extremely niche feature. For me >> it sits just above try-with-resources: not something you use everywhere, >> but when it fits, it removes a class of problems quite effectively. I?ve >> tried to point that out explicitly in the post. >> >> > Having references to other forums does not improve matters. >> >> Two clarifications here: >> Project Coin was a place for proposing language changes, but in practice >> most of the "chosen" features were those already prepared and feasible in >> the time frame. For many other proposals there was only brief, time boxed >> discussion, and we were explicitly asked to stop sending additional ideas. >> The fact that something didn?t make it into that one release does not mean >> it was thoroughly evaluated and rejected on principle. We?ve since seen: >> Glue classes ? a more general/complex analogue of what later appeared as >> extension functions in Kotlin (introduced only a couple of years later). >> "Final interfaces" ? effectively the simplified proposals that Java >> reintroduced in 2020 as sealed types. >> So there is precedent for ideas discussed back then being dropped for >> schedule/complexity reasons, then reappearing later. I reference those >> discussions to show continuity of thought, not as an argument from >> authority. >> >> Reddit references I included mainly to reduce repeated questions and give >> readers a place to see prior objections and clarifications, not as proof >> that the idea is good. >> >> I?ve been looking for areas where this idea would be especially useful, >> and it?s entirely possible that such areas are rare ? or that people are >> simply not used to focusing on this kind of problem. >> >> >> > I'd kick this back to the author to gain experience in writing with the >> idioms and the spirit of the language as using block scope is a basic skill. >> That?s exactly why this concept is here: over the years I?ve found cases >> where relying only on block scope or refactoring was not ideal in practice. >> >> The "classic" example you mention is already in the post: you start with >> simple code, then later add validation, normalization, or security >> constraints. But when bug happen then choice often becomes: >> do a total refactor (new methods, extra scopes, renamed variables, more >> surface area), or do fix without any safeguard against misusing the old >> variables - this can mean a difference of days. >> In real projects, especially under time pressure, people very often >> choose the second option. "forget" keeps diffs small, preserves history, >> and still gives a hard safety guarantee - without introducing additional >> risks. >> >> >> >> -- >> Greetings >> Marek Kozie? ( Lasu ) >> > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Jan 27 17:33:32 2026 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 27 Jan 2026 18:33:32 +0100 Subject: Explicit end-of-use: the 'forget' keyword In-Reply-To: References: Message-ID: I think, if we want to find a problem to solve (already kinda starts bad), then that example is not really the best, because either I can see that it is the last use, if for some reason, this is a complicated method, then you can likely refactor the subsequent part into a separate method to make it cleaner. That is, in that example, the "forget" or "last use" (or whatever alternative name it would go by) is just trying to save on code cleanup (not the best thing to promote imho). If we need a problem to be solved, then it is usually because you have a parameter which you normalize, and now people should not use the old one. That is, you want to shade the old variable. I don't think "forget" is a good way to address this problem. And normally, you can still factor out the last part into a separate method easily. Archie Cobbs ezt ?rta (id?pont: 2026. jan. 27., K, 17:32): > I feel the need to push back a little bit against the idea-bashing here... > > I completely agree that adding a new "forget" keyword to the Java language > doesn't meet the required cost/benefit standard. > > However that's a separate criticism from the underlying goal, which I like > - i.e., help simplify the mental accounting that developers must juggle in > their heads. > > If I am looking at a method that starts like this: > > void method() { > > // Grab a highly sensitive object > final NuclearLaunchButton btn = NuclearLaunchButton.instance(); > ... > > // Do other stuff... > ... > } > > It would be nice to know as soon as possible when "btn" is no longer going > to be used in that method. > > So this is a perfectly appropriate task for IDE's or static code checkers, > e.g.: > > void method() { > > // Grab a highly sensitive object > final NuclearLaunchButton btn = NuclearLaunchButton.instance(); > > // Check status > final boolean armed = btn.isArmed(); // *@last-use(btn)* > > // Do other stuff... > } > > -Archie > -------------- next part -------------- An HTML attachment was scrubbed... URL: From steffenyount at gmail.com Wed Jan 28 00:28:40 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Tue, 27 Jan 2026 16:28:40 -0800 Subject: =?UTF-8?Q?Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java=E2=80=99s?= =?UTF-8?Q?_Static_Polymorphism_and_ServiceLoader_Facilities?= Message-ID: The recent thread *"Java Language Enhancement: Disallow access to static members via object references"* highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via *Static Service Traits*, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "*growing the java language*" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "*Paths to Support Additional Numeric Types on the Java Platform*" presentation. == Static Service Traits for Java == I propose a system of *Static Service Traits*. I use the term "Trait" advisedly: this feature adopts a rigorous *Coherence Model* (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. 1. The service Contextual Keyword We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. 2. Static Implementations and Extension Methods - *Static Implementations:* - *In Interface Headers:* interface MyTrait implements ServiceX. Methods are fulfilled as static. - *In Class Headers:* class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. - *Static Extension Methods:* Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. - *Ergonomic Aliases:* To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). 3. Operational Mechanics & Link-Time Integration A *ServiceLoader Controller* is integrated into the JVM?s class-loading pipeline. During class definition, the Controller *eagerly extracts all relevant metadata* to populate the Static Service Provider Registry, including: - Header-level static implements and implements declarations. - Service binding descriptors from module-info.class. - META-INF/services/ provider-configuration files. *Hierarchical Precedence Resolution:* To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: 1. *Tier 1: Type Specialization:* Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. 2. *Tier 2: Physical Locality:* Provider in the same file (.jar/.class) as the caller wins. 3. *Tier 3: Loader Proximity:* Nearest ClassLoader in the delegation path wins. 4. *Tier 4: Modular Topology:* Internal > Explicit > java.base > Transitive > Automatic. 5. *Tier 5: Sequential Order:* Final tie-breaker via Classpath order. 4. Coherence, The Orphan Rule, and Quarantining To achieve the type-safety of a trait system, we enforce an adapted *Orphan Rule*: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. - *Coherence Enforcement:* Violations in modular code trigger a LinkageError. - *Behavioral Continuity:* Violations in classpath code trigger a load-time warning and the provider is *quarantined* from the Static Registry. To ensure continuity, quarantined providers *remain accessible via existing java.util.ServiceLoader API calls*, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. 5. Performance and AOT Considerations This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with *Project Leyden* and *GraalVM*, as precedence can be "baked" into the binary during AOT compilation. Conclusion By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. Thoughts? -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Wed Jan 28 15:45:03 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Wed, 28 Jan 2026 15:45:03 +0000 Subject: =?utf-8?B?UmU6IFByb3Bvc2FsOiBTdGF0aWMgU2VydmljZSBUcmFpdHPigJRFbmhhbmNp?= =?utf-8?B?bmcgSmF2YeKAmXMgU3RhdGljIFBvbHltb3JwaGlzbSBhbmQgU2VydmljZUxv?= =?utf-8?Q?ader_Facilities?= In-Reply-To: References: Message-ID: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. What was the problem you *personally* ran into? How bad were its implications? How did you work around it? With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. ? Ron > On 28 Jan 2026, at 00:28, Steffen Yount wrote: > > The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. > > By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. > > == Static Service Traits for Java == > > I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. > 1. The service Contextual Keyword > We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. > > 2. Static Implementations and Extension Methods > ? Static Implementations: > ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. > ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. > ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. > ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). > > 3. Operational Mechanics & Link-Time Integration > A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: > ? Header-level static implements and implements declarations. > ? Service binding descriptors from module-info.class. > ? META-INF/services/ provider-configuration files. > Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: > ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. > ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. > ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. > ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > 4. Coherence, The Orphan Rule, and Quarantining > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. > ? Coherence Enforcement: Violations in modular code trigger a LinkageError. > ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. > 5. Performance and AOT Considerations > This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. > Conclusion > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. > > > Thoughts? From forax at univ-mlv.fr Wed Jan 28 16:59:21 2026 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 28 Jan 2026 17:59:21 +0100 (CET) Subject: =?utf-8?Q?Re:_Proposal:_Static_Service_Traits=E2=80=94Enhancing_Java?= =?utf-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: References: Message-ID: <128823063.28089828.1769619561705.JavaMail.zimbra@univ-eiffel.fr> Hello, Alex Buckley has a nice presentation about how to use modules and services. [1] And you can then binds the services using jlink. regards, R?mi [1] https://www.youtube.com/watch?v=RjVjm4uuMvc > From: "Steffen Yount" > To: "amber-dev" > Sent: Wednesday, January 28, 2026 1:28:40 AM > Subject: Proposal: Static Service Traits?Enhancing Java?s Static Polymorphism > and ServiceLoader Facilities > The recent thread "Java Language Enhancement: Disallow access to static members > via object references" highlights a long-standing tension in Java's handling of > static members. While that thread seeks to further decouple instance state from > static logic, I would like to propose moving in the opposite direction: > "doubling down" on Java?s compile-time and link-time static polymorphism. > By beefing up java.util.ServiceLoader facilities and integrating its discovery > mechanism directly into the language via Static Service Traits , we can > facilitate the "Witness Object" paradigm discussed by Brian Goetz's " growing > the java language " presentation and the algebraic "well-known interface" model > for custom numeric types (like Float16 ) proposed in Joe Darcy's " Paths to > Support Additional Numeric Types on the Java Platform " presentation. > == Static Service Traits for Java == > I propose a system of Static Service Traits . I use the term "Trait" advisedly: > this feature adopts a rigorous Coherence Model (inspired by systems like Rust) > to ensure that service resolution is not merely a dynamic search, but a > type-safe, deterministic binding of static capabilities to types. > 1. The service Contextual Keyword > We introduce service as a contextual modifier for interface declarations. > Marking an interface as a service identifies it as a "service type" with a > contract for static capabilities and a high-performance service provider > registry. > 2. Static Implementations and Extension Methods > * Static Implementations: > * In Interface Headers: interface MyTrait implements ServiceX . Methods are > fulfilled as static . > * In Class Headers: class MyClass implements static Numeric . Methods > are implemented as static on the class. Existing signature rules prevent a > method from being both a static and an instance implementation simultaneously. > * Static Extension Methods: Desugared at the call site. myInstance.method() > becomes MyClass.method(myInstance) . Notably, if myInstance is null , it > desugars to MyClass.method(null) without an immediate NullPointerException . > * Ergonomic Aliases: To simplify signatures, we introduce private nested static > type aliases This and Super (e.g., static This add(This a, This b) ). > 3. Operational Mechanics & Link-Time Integration > A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. > During class definition, the Controller eagerly extracts all relevant metadata > to populate the Static Service Provider Registry, including: > * Header-level static implements and implements declarations. > * Service binding descriptors from module-info.class . > * META-INF/services/ provider-configuration files. > Hierarchical Precedence Resolution: To ensure deterministic binding, the > Controller resolves call sites to their most specific service provider via a > waterfall dispatch model: > 1. Tier 1: Type Specialization: Most specific generic match wins , applying the > same scrutiny and rules currently used for existing static overloaded method > resolution. > 2. Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the > caller wins. > 3. Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > 4. Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > > Automatic . > 5. Tier 5: Sequential Order: Final tie-breaker via Classpath order. > 4. Coherence, The Orphan Rule, and Quarantining > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule > : A module (or package on the classpath) must own either the service interface > or the target type to define an implementation. > * Coherence Enforcement: Violations in modular code trigger a LinkageError . > * Behavioral Continuity: Violations in classpath code trigger a load-time > warning and the provider is quarantined from the Static Registry. To ensure > continuity, quarantined providers remain accessible via existing > java.util.ServiceLoader API calls , protecting legacy iteration-based discovery > while ensuring the integrity of the new link-time dispatch. > 5. Performance and AOT Considerations > This model transforms ServiceLoader into a link-time resolver. JIT compilers can > treat service calls as direct invokestatic instructions, enabling aggressive > optimization. This is highly compatible with Project Leyden and GraalVM , as > precedence can be "baked" into the binary during AOT compilation. > Conclusion > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, > high-performance path for algebraic types and witness-based generics. This > allows Java to "grow" through libraries?fulfilling the goals of both Darcy and > Goetz?while maintaining the performance and stability characteristics of the > modern JVM. > Thoughts? -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Wed Jan 28 17:50:42 2026 From: ethan at mccue.dev (Ethan McCue) Date: Wed, 28 Jan 2026 12:50:42 -0500 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> Message-ID: I am sympathetic because "service loader" was also what entered my mind when thinking about "implementation of interface made magically discoverable." Right now an implementation of interface provided via the service loader 1. Can have 0-N implementations provided 2. Has these provided interfaces specified in module info via "provides ... with ...;" An implementation of an interface provided via a witness 1. Should only have 1 implementation for a type being "summoned" 2. Has these provided interfaces specified inside a class, in the current prototype via "__witness" 3. Relies on the interface for which a witness is provided having a generic parameter My gut feeling is that these mechanisms *could* be unified provides Formatter for List with SomeClass; And that, if they aren't, we'd start to see things like summon(Logger).log(...) But equally I find it hard to 1. Reason about how moving "witness registration" from classes to a module info would affect things like witness resolution or "unconditional provides." 2. Justify it beyond a vague feeling On Wed, Jan 28, 2026, 11:56?AM Ron Pressler wrote: > The hardest part in designing and evolving a language is deciding which > problems are important enough to merit a solution in the language and how > their priorities compares to other problems. It?s the hardest part because > the language team are expert at coming up with solutions, but they may not > always know what problems people enoucnter in the field, how frequently > they encounter them, and how they work around them today. > > I?m sure there is some problem hidden here and in your previous post, but > it is not articulated well and is hidden in a poposed solution, even though > no solution is even worth exploring before understanding the problem. And > so the best way to get to a solution is for you to focus on the problem and > only on the problem. > > What was the problem you *personally* ran into? How bad were its > implications? How did you work around it? > > With the hard part done, the JDK team will then be able to assess its > severity and think whether it merits a solution in the JDK, if so, where > (language, libraries, or VM), and how to prioritise it against other > problems worth tackling. Then they?ll be able to propose a solution, and > that?s would be the time to try it out and discuss it. > > ? Ron > > > > > On 28 Jan 2026, at 00:28, Steffen Yount wrote: > > > > The recent thread "Java Language Enhancement: Disallow access to static > members via object references" highlights a long-standing tension in Java's > handling of static members. While that thread seeks to further decouple > instance state from static logic, I would like to propose moving in the > opposite direction: "doubling down" on Java?s compile-time and link-time > static polymorphism. > > > > By beefing up java.util.ServiceLoader facilities and integrating its > discovery mechanism directly into the language via Static Service Traits, > we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's > "growing the java language" presentation and the algebraic "well-known > interface" model for custom numeric types (like Float16) proposed in Joe > Darcy's "Paths to Support Additional Numeric Types on the Java Platform" > presentation. > > > > == Static Service Traits for Java == > > > > I propose a system of Static Service Traits. I use the term "Trait" > advisedly: this feature adopts a rigorous Coherence Model (inspired by > systems like Rust) to ensure that service resolution is not merely a > dynamic search, but a type-safe, deterministic binding of static > capabilities to types. > > 1. The service Contextual Keyword > > We introduce service as a contextual modifier for interface > declarations. Marking an interface as a service identifies it as a "service > type" with a contract for static capabilities and a high-performance > service provider registry. > > > > 2. Static Implementations and Extension Methods > > ? Static Implementations: > > ? In Interface Headers: interface MyTrait implements > ServiceX. Methods are fulfilled as static. > > ? In Class Headers: class MyClass implements static > Numeric. Methods are implemented as static on the class. Existing > signature rules prevent a method from being both a static and an instance > implementation simultaneously. > > ? Static Extension Methods: Desugared at the call site. > myInstance.method() becomes MyClass.method(myInstance). Notably, if > myInstance is null, it desugars to MyClass.method(null) without an > immediate NullPointerException. > > ? Ergonomic Aliases: To simplify signatures, we introduce private > nested static type aliases This and Super (e.g., static This add(This a, > This b)). > > > > 3. Operational Mechanics & Link-Time Integration > > A ServiceLoader Controller is integrated into the JVM?s class-loading > pipeline. During class definition, the Controller eagerly extracts all > relevant metadata to populate the Static Service Provider Registry, > including: > > ? Header-level static implements and implements declarations. > > ? Service binding descriptors from module-info.class. > > ? META-INF/services/ provider-configuration files. > > Hierarchical Precedence Resolution: To ensure deterministic binding, the > Controller resolves call sites to their most specific service provider via > a waterfall dispatch model: > > ? Tier 1: Type Specialization: Most specific generic match wins, > applying the same scrutiny and rules currently used for existing static > overloaded method resolution. > > ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) > as the caller wins. > > ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation > path wins. > > ? Tier 4: Modular Topology: Internal > Explicit > java.base > > Transitive > Automatic. > > ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > > > 4. Coherence, The Orphan Rule, and Quarantining > > To achieve the type-safety of a trait system, we enforce an adapted > Orphan Rule: A module (or package on the classpath) must own either the > service interface or the target type to define an implementation. > > ? Coherence Enforcement: Violations in modular code trigger a > LinkageError. > > ? Behavioral Continuity: Violations in classpath code trigger a > load-time warning and the provider is quarantined from the Static Registry. > To ensure continuity, quarantined providers remain accessible via existing > java.util.ServiceLoader API calls, protecting legacy iteration-based > discovery while ensuring the integrity of the new link-time dispatch. > > 5. Performance and AOT Considerations > > This model transforms ServiceLoader into a link-time resolver. JIT > compilers can treat service calls as direct invokestatic instructions, > enabling aggressive optimization. This is highly compatible with Project > Leyden and GraalVM, as precedence can be "baked" into the binary during AOT > compilation. > > Conclusion > > By transitioning ServiceLoader to a link-time resolver, we provide a > type-safe, high-performance path for algebraic types and witness-based > generics. This allows Java to "grow" through libraries?fulfilling the goals > of both Darcy and Goetz?while maintaining the performance and stability > characteristics of the modern JVM. > > > > > > Thoughts? > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Wed Jan 28 22:08:32 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Wed, 28 Jan 2026 22:08:32 +0000 Subject: =?utf-8?B?UmU6IFtFeHRlcm5hbF0gOiBSZTogUHJvcG9zYWw6IFN0YXRpYyBTZXJ2aWNl?= =?utf-8?B?IFRyYWl0c+KAlEVuaGFuY2luZyBKYXZh4oCZcyBTdGF0aWMgUG9seW1vcnBo?= =?utf-8?Q?ism_and_ServiceLoader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> Message-ID: What is the problem you?re trying to point out? Is it ?something that can be expressed in Java could be expressed more elegantly?? I?m not saying this is a disqualified problem, but I?d like to understand if there?s a problem here that can be stated without hinting at a specific solution (such as "mechanism X and Y could be unified"), as in ?processing pure data in Java is cumbersome?, or ?calling native code from Java is tedious and error-prone?, or ?it?s difficult in Java to clear, debuggable/proflable concurrent code that scales well?, or ?processing some Java collections incurs many cache-misses that impact performance?, or ?propagating timeouts and cancellation across dependent tasks is difficult?, or ?correctly freeing OS resources in the presence of exceptions is difficult? etc. (but with more elaboration, obviously). ? Ron > On 28 Jan 2026, at 17:50, Ethan McCue wrote: > > I am sympathetic because "service loader" was also what entered my mind when thinking about "implementation of interface made magically discoverable." > > Right now an implementation of interface provided via the service loader > > 1. Can have 0-N implementations provided > 2. Has these provided interfaces specified in module info via "provides ... with ...;" > > An implementation of an interface provided via a witness > > 1. Should only have 1 implementation for a type being "summoned" > 2. Has these provided interfaces specified inside a class, in the current prototype via "__witness" > 3. Relies on the interface for which a witness is provided having a generic parameter > > My gut feeling is that these mechanisms *could* be unified > > provides Formatter for List with SomeClass; > > And that, if they aren't, we'd start to see things like > > summon(Logger).log(...) > > But equally I find it hard to > > 1. Reason about how moving "witness registration" from classes to a module info would affect things like witness resolution or "unconditional provides." > 2. Justify it beyond a vague feeling > > > > On Wed, Jan 28, 2026, 11:56?AM Ron Pressler wrote: > The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. > > I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. > > What was the problem you *personally* ran into? How bad were its implications? How did you work around it? > > With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. > > ? Ron > > > > > On 28 Jan 2026, at 00:28, Steffen Yount wrote: > > > > The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. > > > > By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. > > > > == Static Service Traits for Java == > > > > I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. > > 1. The service Contextual Keyword > > We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. > > > > 2. Static Implementations and Extension Methods > > ? Static Implementations: > > ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. > > ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. > > ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. > > ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). > > > > 3. Operational Mechanics & Link-Time Integration > > A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: > > ? Header-level static implements and implements declarations. > > ? Service binding descriptors from module-info.class. > > ? META-INF/services/ provider-configuration files. > > Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: > > ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. > > ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. > > ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > > ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. > > ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > > > 4. Coherence, The Orphan Rule, and Quarantining > > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. > > ? Coherence Enforcement: Violations in modular code trigger a LinkageError. > > ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. > > 5. Performance and AOT Considerations > > This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. > > Conclusion > > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. > > > > > > Thoughts? > From ethan at mccue.dev Wed Jan 28 22:25:18 2026 From: ethan at mccue.dev (Ethan McCue) Date: Wed, 28 Jan 2026 17:25:18 -0500 Subject: =?UTF-8?Q?Re=3A_=5BExternal=5D_=3A_Re=3A_Proposal=3A_Static_Service_Traits?= =?UTF-8?Q?=E2=80=94Enhancing_Java=E2=80=99s_Static_Polymorphism_and_ServiceLoader_?= =?UTF-8?Q?Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> Message-ID: Not in what I just wrote, no. On Wed, Jan 28, 2026, 5:08?PM Ron Pressler wrote: > What is the problem you?re trying to point out? Is it ?something that can > be expressed in Java could be expressed more elegantly?? > > I?m not saying this is a disqualified problem, but I?d like to understand > if there?s a problem here that can be stated without hinting at a specific > solution (such as "mechanism X and Y could be unified"), as in ?processing > pure data in Java is cumbersome?, or ?calling native code from Java is > tedious and error-prone?, or ?it?s difficult in Java to clear, > debuggable/proflable concurrent code that scales well?, or ?processing some > Java collections incurs many cache-misses that impact performance?, or > ?propagating timeouts and cancellation across dependent tasks is > difficult?, or ?correctly freeing OS resources in the presence of > exceptions is difficult? etc. (but with more elaboration, obviously). > > ? Ron > > > On 28 Jan 2026, at 17:50, Ethan McCue wrote: > > > > I am sympathetic because "service loader" was also what entered my mind > when thinking about "implementation of interface made magically > discoverable." > > > > Right now an implementation of interface provided via the service loader > > > > 1. Can have 0-N implementations provided > > 2. Has these provided interfaces specified in module info via "provides > ... with ...;" > > > > An implementation of an interface provided via a witness > > > > 1. Should only have 1 implementation for a type being "summoned" > > 2. Has these provided interfaces specified inside a class, in the > current prototype via "__witness" > > 3. Relies on the interface for which a witness is provided having a > generic parameter > > > > My gut feeling is that these mechanisms *could* be unified > > > > provides Formatter for List with SomeClass; > > > > And that, if they aren't, we'd start to see things like > > > > summon(Logger).log(...) > > > > But equally I find it hard to > > > > 1. Reason about how moving "witness registration" from classes to a > module info would affect things like witness resolution or "unconditional > provides." > > 2. Justify it beyond a vague feeling > > > > > > > > On Wed, Jan 28, 2026, 11:56?AM Ron Pressler > wrote: > > The hardest part in designing and evolving a language is deciding which > problems are important enough to merit a solution in the language and how > their priorities compares to other problems. It?s the hardest part because > the language team are expert at coming up with solutions, but they may not > always know what problems people enoucnter in the field, how frequently > they encounter them, and how they work around them today. > > > > I?m sure there is some problem hidden here and in your previous post, > but it is not articulated well and is hidden in a poposed solution, even > though no solution is even worth exploring before understanding the > problem. And so the best way to get to a solution is for you to focus on > the problem and only on the problem. > > > > What was the problem you *personally* ran into? How bad were its > implications? How did you work around it? > > > > With the hard part done, the JDK team will then be able to assess its > severity and think whether it merits a solution in the JDK, if so, where > (language, libraries, or VM), and how to prioritise it against other > problems worth tackling. Then they?ll be able to propose a solution, and > that?s would be the time to try it out and discuss it. > > > > ? Ron > > > > > > > > > On 28 Jan 2026, at 00:28, Steffen Yount > wrote: > > > > > > The recent thread "Java Language Enhancement: Disallow access to > static members via object references" highlights a long-standing tension in > Java's handling of static members. While that thread seeks to further > decouple instance state from static logic, I would like to propose moving > in the opposite direction: "doubling down" on Java?s compile-time and > link-time static polymorphism. > > > > > > By beefing up java.util.ServiceLoader facilities and integrating its > discovery mechanism directly into the language via Static Service Traits, > we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's > "growing the java language" presentation and the algebraic "well-known > interface" model for custom numeric types (like Float16) proposed in Joe > Darcy's "Paths to Support Additional Numeric Types on the Java Platform" > presentation. > > > > > > == Static Service Traits for Java == > > > > > > I propose a system of Static Service Traits. I use the term "Trait" > advisedly: this feature adopts a rigorous Coherence Model (inspired by > systems like Rust) to ensure that service resolution is not merely a > dynamic search, but a type-safe, deterministic binding of static > capabilities to types. > > > 1. The service Contextual Keyword > > > We introduce service as a contextual modifier for interface > declarations. Marking an interface as a service identifies it as a "service > type" with a contract for static capabilities and a high-performance > service provider registry. > > > > > > 2. Static Implementations and Extension Methods > > > ? Static Implementations: > > > ? In Interface Headers: interface MyTrait implements > ServiceX. Methods are fulfilled as static. > > > ? In Class Headers: class MyClass implements static > Numeric. Methods are implemented as static on the class. Existing > signature rules prevent a method from being both a static and an instance > implementation simultaneously. > > > ? Static Extension Methods: Desugared at the call site. > myInstance.method() becomes MyClass.method(myInstance). Notably, if > myInstance is null, it desugars to MyClass.method(null) without an > immediate NullPointerException. > > > ? Ergonomic Aliases: To simplify signatures, we introduce private > nested static type aliases This and Super (e.g., static This add(This a, > This b)). > > > > > > 3. Operational Mechanics & Link-Time Integration > > > A ServiceLoader Controller is integrated into the JVM?s class-loading > pipeline. During class definition, the Controller eagerly extracts all > relevant metadata to populate the Static Service Provider Registry, > including: > > > ? Header-level static implements and implements declarations. > > > ? Service binding descriptors from module-info.class. > > > ? META-INF/services/ provider-configuration files. > > > Hierarchical Precedence Resolution: To ensure deterministic binding, > the Controller resolves call sites to their most specific service provider > via a waterfall dispatch model: > > > ? Tier 1: Type Specialization: Most specific generic match wins, > applying the same scrutiny and rules currently used for existing static > overloaded method resolution. > > > ? Tier 2: Physical Locality: Provider in the same file > (.jar/.class) as the caller wins. > > > ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation > path wins. > > > ? Tier 4: Modular Topology: Internal > Explicit > java.base > > Transitive > Automatic. > > > ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > > > > > 4. Coherence, The Orphan Rule, and Quarantining > > > To achieve the type-safety of a trait system, we enforce an adapted > Orphan Rule: A module (or package on the classpath) must own either the > service interface or the target type to define an implementation. > > > ? Coherence Enforcement: Violations in modular code trigger a > LinkageError. > > > ? Behavioral Continuity: Violations in classpath code trigger a > load-time warning and the provider is quarantined from the Static Registry. > To ensure continuity, quarantined providers remain accessible via existing > java.util.ServiceLoader API calls, protecting legacy iteration-based > discovery while ensuring the integrity of the new link-time dispatch. > > > 5. Performance and AOT Considerations > > > This model transforms ServiceLoader into a link-time resolver. JIT > compilers can treat service calls as direct invokestatic instructions, > enabling aggressive optimization. This is highly compatible with Project > Leyden and GraalVM, as precedence can be "baked" into the binary during AOT > compilation. > > > Conclusion > > > By transitioning ServiceLoader to a link-time resolver, we provide a > type-safe, high-performance path for algebraic types and witness-based > generics. This allows Java to "grow" through libraries?fulfilling the goals > of both Darcy and Goetz?while maintaining the performance and stability > characteristics of the modern JVM. > > > > > > > > > Thoughts? > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From steffenyount at gmail.com Wed Jan 28 23:20:15 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Wed, 28 Jan 2026 15:20:15 -0800 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> Message-ID: Hi Ron, Thank you for the feedback. It's totally reasonable to push for the "why" before getting into the "how." The personal problem I encounter is the severe and inconvenient extensibility costs of the current language model. When pursuing data-oriented design or domain-specific numeric types in Java, I find the language's current facilities to be a significant design obstacle. These are not just issues I find while building, but constraints that fundamentally alter how I consider a project's architecture before a single line of code is written. I have encountered the following specific friction points: The Instantiation Tax: On several occasions, I have turned to Java for intensive math calculations. These efforts typically start small but eventually outgrow Java's supported numeric abstractions. To move beyond small memory footprints and 64-bit representational limits, I find I must refactor to contiguous arrays, reusable Flyweight objects, and custom math methods just to manage the memory pressure and data type limitations. Because I cannot define polymorphic static contracts for these types, I am forced to pay an Instantiation Tax?maintaining "witness" objects just to access static logic. The ergonomic noise and heap-inefficiency of the unwanted object headers are so high that I am often discouraged from pursuing my original abstractions entirely. While I have used primitive long types as witnesses for static overloaded method binding, using the NewType pattern for type-safe bindings remains prohibitively expensive; a new NewType class's implementation immediately excludes it from participation within the language's built-in expression operators. In the aforementioned efforts, I have ultimately abandoned Java for C/C++ simply because they allowed me to shape the data layout and its static behavior without this "abstraction tax." The Expression Problem (Post-hoc Abstraction): When I find it necessary to treat third-party classes as part of a common abstraction?for instance, when attempting to resolve Guice-based injection or AOP design issues without the "magic" of runtime reflection?the traditional path is the Adapter Pattern. I find this route unsustainable; creating a new wrapper class for every instance not only fragments object identity but generates significant GC churn. I have seen production code with wrapper classes nested eight levels deep just to satisfy disparate abstractions. The ability to implement type level contracts rather than just instance level contracts, along with type level extension methods, would allow us to side-step the wrapper classes with implementations that bind to existing types without modifying their source code. The lack of them serves as a wall preventing me from designing the clean, type-safe, and AOT-friendly systems I know are possible elsewhere. The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more of a library than a language feature. It requires explicit custom code to verify bindings at startup for fail-fast behavior. The API remains an extra invocation hurdle with its lookup and instantiation requirements. A coherent language-integrated, static service interface method dispatch and binding would dramatically reduce this ceremony and increase utility by moving it from a manual runtime search to a link-time certainty. My "big picture" problem is that Java?s evolution model currently makes it difficult to "grow the language" via libraries that feel native and are performan, such as the recent prototype exploration of Float16. I believe the language should provide the infrastructure for _Static Service Traits_ or otherwise make that kind of library-driven growth a standard capability for all developers. I feel "corralled" into 1990s instance-based OOP. When I explore data-oriented design or high-performance numeric abstractions, the features found in my competitors' language tool belts would be incredibly useful; without them, I find myself looking at alternate language implementations just to avoid Java's structural obstacles. Given that Project Amber?s stated mission is to "right-size language ceremony" and improve developer productivity, doesn't a proposal that eliminates this Instantiation Tax and link-time service ceremony seem like a relevant and worthy pursuit? -Steffen On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler wrote: > > The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. > > I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. > > What was the problem you *personally* ran into? How bad were its implications? How did you work around it? > > With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. > > ? Ron > > > > > On 28 Jan 2026, at 00:28, Steffen Yount wrote: > > > > The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. > > > > By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. > > > > == Static Service Traits for Java == > > > > I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. > > 1. The service Contextual Keyword > > We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. > > > > 2. Static Implementations and Extension Methods > > ? Static Implementations: > > ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. > > ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. > > ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. > > ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). > > > > 3. Operational Mechanics & Link-Time Integration > > A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: > > ? Header-level static implements and implements declarations. > > ? Service binding descriptors from module-info.class. > > ? META-INF/services/ provider-configuration files. > > Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: > > ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. > > ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. > > ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > > ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. > > ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > > > 4. Coherence, The Orphan Rule, and Quarantining > > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. > > ? Coherence Enforcement: Violations in modular code trigger a LinkageError. > > ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. > > 5. Performance and AOT Considerations > > This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. > > Conclusion > > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. > > > > > > Thoughts? > From steffenyount at gmail.com Thu Jan 29 00:13:43 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Wed, 28 Jan 2026 16:13:43 -0800 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <128823063.28089828.1769619561705.JavaMail.zimbra@univ-eiffel.fr> References: <128823063.28089828.1769619561705.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thanks, that is a great presentation! Regards, Steffen On Wed, Jan 28, 2026 at 8:59?AM Remi Forax wrote: > > Hello, > Alex Buckley has a nice presentation about how to use modules and services. [1] > > And you can then binds the services using jlink. > > regards, > R?mi > > [1] https://www.youtube.com/watch?v=RjVjm4uuMvc > > ________________________________ > > From: "Steffen Yount" > To: "amber-dev" > Sent: Wednesday, January 28, 2026 1:28:40 AM > Subject: Proposal: Static Service Traits?Enhancing Java?s Static Polymorphism and ServiceLoader Facilities > > The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. > > By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. > > == Static Service Traits for Java == > > I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. > > 1. The service Contextual Keyword > We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. > > 2. Static Implementations and Extension Methods > > Static Implementations: > > In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. > In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. > > Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. > Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). > > > 3. Operational Mechanics & Link-Time Integration > A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: > > Header-level static implements and implements declarations. > Service binding descriptors from module-info.class. > META-INF/services/ provider-configuration files. > > Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: > > Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. > Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. > Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. > Tier 5: Sequential Order: Final tie-breaker via Classpath order. > > > 4. Coherence, The Orphan Rule, and Quarantining > To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. > > Coherence Enforcement: Violations in modular code trigger a LinkageError. > Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. > > 5. Performance and AOT Considerations > This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. > > Conclusion > By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. > > > Thoughts? > From ron.pressler at oracle.com Thu Jan 29 00:31:29 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Thu, 29 Jan 2026 00:31:29 +0000 Subject: =?utf-8?B?UmU6IFByb3Bvc2FsOiBTdGF0aWMgU2VydmljZSBUcmFpdHPigJRFbmhhbmNp?= =?utf-8?B?bmcgSmF2YeKAmXMgU3RhdGljIFBvbHltb3JwaGlzbSBhbmQgU2VydmljZUxv?= =?utf-8?Q?ader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> Message-ID: <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> The first two paragraphs boil down to ?it?s difficult to work with user-defined numeric types, especially with a tight memory layout?. As you probably know, this is a problem we?re already working on solving. Aside from that, I think the problem you?re reporting is: "The java.util.ServiceLoader ... requires explicit custom code to verify bindings at startup for fail-fast behavior. The API remains an extra invocation hurdle with its lookup and instantiation requirements.? What you?re saying about ?right-sizing? the language is absolutely right, but for the JDK team to propose a solution to a problem you?ve identified, the problem first needs to be understood. Assuming that what I quoted is the problem you?ve tried expressing, can you elaborate more on: 1. Requires explicit custom code to verify bindings at startup for fail-fast behavior. 2. The API remains an extra invocation hurdle with its lookup and instantiation requirements. The more concrete you are, as in ?I tried to do X and this is the code I wrote?, the easier it would be to understand. I think I know what you mean in #1, but it would be helpful to show what code you had to write. In #2, what is exactly the hurdle? E.g. let?s say that the hurdle is that the API is verbose. That is only a problem if it has to be used lots of times in a program, so what was your program that required so many uses of ServiceLoader? (P.S. ?right-sizing? the language doesn?t mean that anything that could be improved with a language feature should be because every addition to the language complicates it. We like it when a non-trivial language feature can solve a big problem or multiple small ones.) ? Ron > On 28 Jan 2026, at 23:20, Steffen Yount wrote: > > Hi Ron, > > Thank you for the feedback. It's totally reasonable to push for the > "why" before getting into the "how." > > The personal problem I encounter is the severe and inconvenient > extensibility costs of the current language model. When pursuing > data-oriented design or domain-specific numeric types in Java, I find > the language's current facilities to be a significant design obstacle. > These are not just issues I find while building, but constraints that > fundamentally alter how I consider a project's architecture before a > single line of code is written. > > I have encountered the following specific friction points: > > The Instantiation Tax: On several occasions, I have turned to Java for > intensive math calculations. These efforts typically start small but > eventually outgrow Java's supported numeric abstractions. To move > beyond small memory footprints and 64-bit representational limits, I > find I must refactor to contiguous arrays, reusable Flyweight objects, > and custom math methods just to manage the memory pressure and data > type limitations. Because I cannot define polymorphic static contracts > for these types, I am forced to pay an Instantiation Tax?maintaining > "witness" objects just to access static logic. The ergonomic noise and > heap-inefficiency of the unwanted object headers are so high that I am > often discouraged from pursuing my original abstractions entirely. > While I have used primitive long types as witnesses for static > overloaded method binding, using the NewType pattern for type-safe > bindings remains prohibitively expensive; a new NewType class's > implementation immediately excludes it from participation within the > language's built-in expression operators. In the aforementioned > efforts, I have ultimately abandoned Java for C/C++ simply because > they allowed me to shape the data layout and its static behavior > without this "abstraction tax." > > The Expression Problem (Post-hoc Abstraction): When I find it > necessary to treat third-party classes as part of a common > abstraction?for instance, when attempting to resolve Guice-based > injection or AOP design issues without the "magic" of runtime > reflection?the traditional path is the Adapter Pattern. I find this > route unsustainable; creating a new wrapper class for every instance > not only fragments object identity but generates significant GC churn. > I have seen production code with wrapper classes nested eight levels > deep just to satisfy disparate abstractions. The ability to implement > type level contracts rather than just instance level contracts, along > with type level extension methods, would allow us to side-step the > wrapper classes with implementations that bind to existing types > without modifying their source code. The lack of them serves as a wall > preventing me from designing the clean, type-safe, and AOT-friendly > systems I know are possible elsewhere. > > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more > of a library than a language feature. It requires explicit custom code > to verify bindings at startup for fail-fast behavior. The API remains > an extra invocation hurdle with its lookup and instantiation > requirements. A coherent language-integrated, static service interface > method dispatch and binding would dramatically reduce this ceremony > and increase utility by moving it from a manual runtime search to a > link-time certainty. > > My "big picture" problem is that Java?s evolution model currently > makes it difficult to "grow the language" via libraries that feel > native and are performan, such as the recent prototype exploration of > Float16. I believe the language should provide the infrastructure for > _Static Service Traits_ or otherwise make that kind of library-driven > growth a standard capability for all developers. > > I feel "corralled" into 1990s instance-based OOP. When I explore > data-oriented design or high-performance numeric abstractions, the > features found in my competitors' language tool belts would be > incredibly useful; without them, I find myself looking at alternate > language implementations just to avoid Java's structural obstacles. > > Given that Project Amber?s stated mission is to "right-size language > ceremony" and improve developer productivity, doesn't a proposal that > eliminates this Instantiation Tax and link-time service ceremony seem > like a relevant and worthy pursuit? > > -Steffen > > > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler wrote: >> >> The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. >> >> I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. >> >> What was the problem you *personally* ran into? How bad were its implications? How did you work around it? >> >> With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. >> >> ? Ron >> >> >> >>> On 28 Jan 2026, at 00:28, Steffen Yount wrote: >>> >>> The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. >>> >>> By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. >>> >>> == Static Service Traits for Java == >>> >>> I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. >>> 1. The service Contextual Keyword >>> We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. >>> >>> 2. Static Implementations and Extension Methods >>> ? Static Implementations: >>> ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. >>> ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. >>> ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. >>> ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). >>> >>> 3. Operational Mechanics & Link-Time Integration >>> A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: >>> ? Header-level static implements and implements declarations. >>> ? Service binding descriptors from module-info.class. >>> ? META-INF/services/ provider-configuration files. >>> Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: >>> ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. >>> ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. >>> >>> 4. Coherence, The Orphan Rule, and Quarantining >>> To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. >>> ? Coherence Enforcement: Violations in modular code trigger a LinkageError. >>> ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. >>> 5. Performance and AOT Considerations >>> This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. >>> Conclusion >>> By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. >>> >>> >>> Thoughts? >> From rotanolexandr842 at gmail.com Thu Jan 29 07:01:34 2026 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Thu, 29 Jan 2026 09:01:34 +0200 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: I may be not exactly at the same topic as Steffen, but I think that, more vaguely speaking, the issue can be more generally described as "lack of compile time metaprogramming for contract verification", which manifests in, for example, such scenarios: 1. No way to verify jackson-using class has exactly one @JsonCreator 2. No way to assert there is exactly one static method that is invokable with given arguments (static traits specifically, for example ensure there is some unambigous metadata-producing method for runtime discovery mechanisms) 3. Or, more generally, no way to ensure that class A implements a word-phrased (on the contrary to method-phrased, which is statically enforced) contract of interface B, such as for SequencedCollection. Of course, this particular example is largely beyond the limits of metaprogramming, but for something simpler such as potential interface "UnmodifiableList" that would have declared that all modifying methods of List in class implementing UnmodifiableList should ultimately throw an exception or at least not mutate backing state fields, static checks are doable. The other thing is that this is what static analysis tools are made for. Unfortunately, not everyone uses even developer-provided analysis profiles when using said developer`s library, so the fact that there is no langauge level contract enforcement capabilities for more complex scenarios surely leads to a lot of unnecessary runtime errors. Though, of course, I admit that what I have described (metaprogrammatic contract enforcement) is a far larger scope then just static methods. Ultimately, even just static-method-contract enforcement would solve a lot of reflection-related issues for me when writing things like runtime discovery On Thu, Jan 29, 2026 at 2:31?AM Ron Pressler wrote: > The first two paragraphs boil down to ?it?s difficult to work with > user-defined numeric types, especially with a tight memory layout?. As you > probably know, this is a problem we?re already working on solving. > > Aside from that, I think the problem you?re reporting is: > > "The java.util.ServiceLoader ... requires explicit custom code to verify > bindings at startup for fail-fast behavior. The API remains an extra > invocation hurdle with its lookup and instantiation requirements.? > > What you?re saying about ?right-sizing? the language is absolutely right, > but for the JDK team to propose a solution to a problem you?ve identified, > the problem first needs to be understood. Assuming that what I quoted is > the problem you?ve tried expressing, can you elaborate more on: > > 1. Requires explicit custom code to verify bindings at startup for > fail-fast behavior. > > 2. The API remains an extra invocation hurdle with its lookup and > instantiation requirements. > > The more concrete you are, as in ?I tried to do X and this is the code I > wrote?, the easier it would be to understand. > I think I know what you mean in #1, but it would be helpful to show what > code you had to write. In #2, what is exactly the hurdle? E.g. let?s say > that the hurdle is that the API is verbose. That is only a problem if it > has to be used lots of times in a program, so what was your program that > required so many uses of ServiceLoader? > > (P.S. ?right-sizing? the language doesn?t mean that anything that could be > improved with a language feature should be because every addition to the > language complicates it. We like it when a non-trivial language feature can > solve a big problem or multiple small ones.) > > ? Ron > > > > On 28 Jan 2026, at 23:20, Steffen Yount wrote: > > > > Hi Ron, > > > > Thank you for the feedback. It's totally reasonable to push for the > > "why" before getting into the "how." > > > > The personal problem I encounter is the severe and inconvenient > > extensibility costs of the current language model. When pursuing > > data-oriented design or domain-specific numeric types in Java, I find > > the language's current facilities to be a significant design obstacle. > > These are not just issues I find while building, but constraints that > > fundamentally alter how I consider a project's architecture before a > > single line of code is written. > > > > I have encountered the following specific friction points: > > > > The Instantiation Tax: On several occasions, I have turned to Java for > > intensive math calculations. These efforts typically start small but > > eventually outgrow Java's supported numeric abstractions. To move > > beyond small memory footprints and 64-bit representational limits, I > > find I must refactor to contiguous arrays, reusable Flyweight objects, > > and custom math methods just to manage the memory pressure and data > > type limitations. Because I cannot define polymorphic static contracts > > for these types, I am forced to pay an Instantiation Tax?maintaining > > "witness" objects just to access static logic. The ergonomic noise and > > heap-inefficiency of the unwanted object headers are so high that I am > > often discouraged from pursuing my original abstractions entirely. > > While I have used primitive long types as witnesses for static > > overloaded method binding, using the NewType pattern for type-safe > > bindings remains prohibitively expensive; a new NewType class's > > implementation immediately excludes it from participation within the > > language's built-in expression operators. In the aforementioned > > efforts, I have ultimately abandoned Java for C/C++ simply because > > they allowed me to shape the data layout and its static behavior > > without this "abstraction tax." > > > > The Expression Problem (Post-hoc Abstraction): When I find it > > necessary to treat third-party classes as part of a common > > abstraction?for instance, when attempting to resolve Guice-based > > injection or AOP design issues without the "magic" of runtime > > reflection?the traditional path is the Adapter Pattern. I find this > > route unsustainable; creating a new wrapper class for every instance > > not only fragments object identity but generates significant GC churn. > > I have seen production code with wrapper classes nested eight levels > > deep just to satisfy disparate abstractions. The ability to implement > > type level contracts rather than just instance level contracts, along > > with type level extension methods, would allow us to side-step the > > wrapper classes with implementations that bind to existing types > > without modifying their source code. The lack of them serves as a wall > > preventing me from designing the clean, type-safe, and AOT-friendly > > systems I know are possible elsewhere. > > > > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more > > of a library than a language feature. It requires explicit custom code > > to verify bindings at startup for fail-fast behavior. The API remains > > an extra invocation hurdle with its lookup and instantiation > > requirements. A coherent language-integrated, static service interface > > method dispatch and binding would dramatically reduce this ceremony > > and increase utility by moving it from a manual runtime search to a > > link-time certainty. > > > > My "big picture" problem is that Java?s evolution model currently > > makes it difficult to "grow the language" via libraries that feel > > native and are performan, such as the recent prototype exploration of > > Float16. I believe the language should provide the infrastructure for > > _Static Service Traits_ or otherwise make that kind of library-driven > > growth a standard capability for all developers. > > > > I feel "corralled" into 1990s instance-based OOP. When I explore > > data-oriented design or high-performance numeric abstractions, the > > features found in my competitors' language tool belts would be > > incredibly useful; without them, I find myself looking at alternate > > language implementations just to avoid Java's structural obstacles. > > > > Given that Project Amber?s stated mission is to "right-size language > > ceremony" and improve developer productivity, doesn't a proposal that > > eliminates this Instantiation Tax and link-time service ceremony seem > > like a relevant and worthy pursuit? > > > > -Steffen > > > > > > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler > wrote: > >> > >> The hardest part in designing and evolving a language is deciding which > problems are important enough to merit a solution in the language and how > their priorities compares to other problems. It?s the hardest part because > the language team are expert at coming up with solutions, but they may not > always know what problems people enoucnter in the field, how frequently > they encounter them, and how they work around them today. > >> > >> I?m sure there is some problem hidden here and in your previous post, > but it is not articulated well and is hidden in a poposed solution, even > though no solution is even worth exploring before understanding the > problem. And so the best way to get to a solution is for you to focus on > the problem and only on the problem. > >> > >> What was the problem you *personally* ran into? How bad were its > implications? How did you work around it? > >> > >> With the hard part done, the JDK team will then be able to assess its > severity and think whether it merits a solution in the JDK, if so, where > (language, libraries, or VM), and how to prioritise it against other > problems worth tackling. Then they?ll be able to propose a solution, and > that?s would be the time to try it out and discuss it. > >> > >> ? Ron > >> > >> > >> > >>> On 28 Jan 2026, at 00:28, Steffen Yount > wrote: > >>> > >>> The recent thread "Java Language Enhancement: Disallow access to > static members via object references" highlights a long-standing tension in > Java's handling of static members. While that thread seeks to further > decouple instance state from static logic, I would like to propose moving > in the opposite direction: "doubling down" on Java?s compile-time and > link-time static polymorphism. > >>> > >>> By beefing up java.util.ServiceLoader facilities and integrating its > discovery mechanism directly into the language via Static Service Traits, > we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's > "growing the java language" presentation and the algebraic "well-known > interface" model for custom numeric types (like Float16) proposed in Joe > Darcy's "Paths to Support Additional Numeric Types on the Java Platform" > presentation. > >>> > >>> == Static Service Traits for Java == > >>> > >>> I propose a system of Static Service Traits. I use the term "Trait" > advisedly: this feature adopts a rigorous Coherence Model (inspired by > systems like Rust) to ensure that service resolution is not merely a > dynamic search, but a type-safe, deterministic binding of static > capabilities to types. > >>> 1. The service Contextual Keyword > >>> We introduce service as a contextual modifier for interface > declarations. Marking an interface as a service identifies it as a "service > type" with a contract for static capabilities and a high-performance > service provider registry. > >>> > >>> 2. Static Implementations and Extension Methods > >>> ? Static Implementations: > >>> ? In Interface Headers: interface MyTrait implements > ServiceX. Methods are fulfilled as static. > >>> ? In Class Headers: class MyClass implements static > Numeric. Methods are implemented as static on the class. Existing > signature rules prevent a method from being both a static and an instance > implementation simultaneously. > >>> ? Static Extension Methods: Desugared at the call site. > myInstance.method() becomes MyClass.method(myInstance). Notably, if > myInstance is null, it desugars to MyClass.method(null) without an > immediate NullPointerException. > >>> ? Ergonomic Aliases: To simplify signatures, we introduce private > nested static type aliases This and Super (e.g., static This add(This a, > This b)). > >>> > >>> 3. Operational Mechanics & Link-Time Integration > >>> A ServiceLoader Controller is integrated into the JVM?s class-loading > pipeline. During class definition, the Controller eagerly extracts all > relevant metadata to populate the Static Service Provider Registry, > including: > >>> ? Header-level static implements and implements declarations. > >>> ? Service binding descriptors from module-info.class. > >>> ? META-INF/services/ provider-configuration files. > >>> Hierarchical Precedence Resolution: To ensure deterministic binding, > the Controller resolves call sites to their most specific service provider > via a waterfall dispatch model: > >>> ? Tier 1: Type Specialization: Most specific generic match wins, > applying the same scrutiny and rules currently used for existing static > overloaded method resolution. > >>> ? Tier 2: Physical Locality: Provider in the same file > (.jar/.class) as the caller wins. > >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation > path wins. > >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > > Transitive > Automatic. > >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > >>> > >>> 4. Coherence, The Orphan Rule, and Quarantining > >>> To achieve the type-safety of a trait system, we enforce an adapted > Orphan Rule: A module (or package on the classpath) must own either the > service interface or the target type to define an implementation. > >>> ? Coherence Enforcement: Violations in modular code trigger a > LinkageError. > >>> ? Behavioral Continuity: Violations in classpath code trigger a > load-time warning and the provider is quarantined from the Static Registry. > To ensure continuity, quarantined providers remain accessible via existing > java.util.ServiceLoader API calls, protecting legacy iteration-based > discovery while ensuring the integrity of the new link-time dispatch. > >>> 5. Performance and AOT Considerations > >>> This model transforms ServiceLoader into a link-time resolver. JIT > compilers can treat service calls as direct invokestatic instructions, > enabling aggressive optimization. This is highly compatible with Project > Leyden and GraalVM, as precedence can be "baked" into the binary during AOT > compilation. > >>> Conclusion > >>> By transitioning ServiceLoader to a link-time resolver, we provide a > type-safe, high-performance path for algebraic types and witness-based > generics. This allows Java to "grow" through libraries?fulfilling the goals > of both Darcy and Goetz?while maintaining the performance and stability > characteristics of the modern JVM. > >>> > >>> > >>> Thoughts? > >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From josiahnoel at gmail.com Thu Jan 29 12:54:11 2026 From: josiahnoel at gmail.com (Josiah Noel) Date: Thu, 29 Jan 2026 07:54:11 -0500 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: Can't you write an annotation processor to do all three of those checks during compilation? On Thu, Jan 29, 2026, 2:01?AM Olexandr Rotan wrote: > I may be not exactly at the same topic as Steffen, but I think that, more > vaguely speaking, the issue can be more generally described as "lack of > compile time metaprogramming for contract verification", which manifests > in, for example, such scenarios: > > 1. No way to verify jackson-using class has exactly one @JsonCreator > 2. No way to assert there is exactly one static method that is invokable > with given arguments (static traits specifically, for example ensure there > is some unambigous metadata-producing method for runtime discovery > mechanisms) > 3. Or, more generally, no way to ensure that class A implements a > word-phrased (on the contrary to method-phrased, which is statically > enforced) contract of interface B, such as for SequencedCollection. Of > course, this particular example is largely beyond the limits of > metaprogramming, but for something simpler such as potential interface > "UnmodifiableList" that would have declared that all modifying methods of > List in class implementing UnmodifiableList should ultimately throw an > exception or at least not mutate backing state fields, static checks are > doable. > > The other thing is that this is what static analysis tools are made for. > Unfortunately, not everyone uses even developer-provided analysis profiles > when using said developer`s library, so the fact that there is no langauge > level contract enforcement capabilities for more complex scenarios surely > leads to a lot of unnecessary runtime errors. > > Though, of course, I admit that what I have described (metaprogrammatic > contract enforcement) is a far larger scope then just static methods. > Ultimately, even just static-method-contract enforcement would solve a lot > of reflection-related issues for me when writing things like runtime > discovery > > On Thu, Jan 29, 2026 at 2:31?AM Ron Pressler > wrote: > >> The first two paragraphs boil down to ?it?s difficult to work with >> user-defined numeric types, especially with a tight memory layout?. As you >> probably know, this is a problem we?re already working on solving. >> >> Aside from that, I think the problem you?re reporting is: >> >> "The java.util.ServiceLoader ... requires explicit custom code to verify >> bindings at startup for fail-fast behavior. The API remains an extra >> invocation hurdle with its lookup and instantiation requirements.? >> >> What you?re saying about ?right-sizing? the language is absolutely right, >> but for the JDK team to propose a solution to a problem you?ve identified, >> the problem first needs to be understood. Assuming that what I quoted is >> the problem you?ve tried expressing, can you elaborate more on: >> >> 1. Requires explicit custom code to verify bindings at startup for >> fail-fast behavior. >> >> 2. The API remains an extra invocation hurdle with its lookup and >> instantiation requirements. >> >> The more concrete you are, as in ?I tried to do X and this is the code I >> wrote?, the easier it would be to understand. >> I think I know what you mean in #1, but it would be helpful to show what >> code you had to write. In #2, what is exactly the hurdle? E.g. let?s say >> that the hurdle is that the API is verbose. That is only a problem if it >> has to be used lots of times in a program, so what was your program that >> required so many uses of ServiceLoader? >> >> (P.S. ?right-sizing? the language doesn?t mean that anything that could >> be improved with a language feature should be because every addition to the >> language complicates it. We like it when a non-trivial language feature can >> solve a big problem or multiple small ones.) >> >> ? Ron >> >> >> > On 28 Jan 2026, at 23:20, Steffen Yount wrote: >> > >> > Hi Ron, >> > >> > Thank you for the feedback. It's totally reasonable to push for the >> > "why" before getting into the "how." >> > >> > The personal problem I encounter is the severe and inconvenient >> > extensibility costs of the current language model. When pursuing >> > data-oriented design or domain-specific numeric types in Java, I find >> > the language's current facilities to be a significant design obstacle. >> > These are not just issues I find while building, but constraints that >> > fundamentally alter how I consider a project's architecture before a >> > single line of code is written. >> > >> > I have encountered the following specific friction points: >> > >> > The Instantiation Tax: On several occasions, I have turned to Java for >> > intensive math calculations. These efforts typically start small but >> > eventually outgrow Java's supported numeric abstractions. To move >> > beyond small memory footprints and 64-bit representational limits, I >> > find I must refactor to contiguous arrays, reusable Flyweight objects, >> > and custom math methods just to manage the memory pressure and data >> > type limitations. Because I cannot define polymorphic static contracts >> > for these types, I am forced to pay an Instantiation Tax?maintaining >> > "witness" objects just to access static logic. The ergonomic noise and >> > heap-inefficiency of the unwanted object headers are so high that I am >> > often discouraged from pursuing my original abstractions entirely. >> > While I have used primitive long types as witnesses for static >> > overloaded method binding, using the NewType pattern for type-safe >> > bindings remains prohibitively expensive; a new NewType class's >> > implementation immediately excludes it from participation within the >> > language's built-in expression operators. In the aforementioned >> > efforts, I have ultimately abandoned Java for C/C++ simply because >> > they allowed me to shape the data layout and its static behavior >> > without this "abstraction tax." >> > >> > The Expression Problem (Post-hoc Abstraction): When I find it >> > necessary to treat third-party classes as part of a common >> > abstraction?for instance, when attempting to resolve Guice-based >> > injection or AOP design issues without the "magic" of runtime >> > reflection?the traditional path is the Adapter Pattern. I find this >> > route unsustainable; creating a new wrapper class for every instance >> > not only fragments object identity but generates significant GC churn. >> > I have seen production code with wrapper classes nested eight levels >> > deep just to satisfy disparate abstractions. The ability to implement >> > type level contracts rather than just instance level contracts, along >> > with type level extension methods, would allow us to side-step the >> > wrapper classes with implementations that bind to existing types >> > without modifying their source code. The lack of them serves as a wall >> > preventing me from designing the clean, type-safe, and AOT-friendly >> > systems I know are possible elsewhere. >> > >> > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more >> > of a library than a language feature. It requires explicit custom code >> > to verify bindings at startup for fail-fast behavior. The API remains >> > an extra invocation hurdle with its lookup and instantiation >> > requirements. A coherent language-integrated, static service interface >> > method dispatch and binding would dramatically reduce this ceremony >> > and increase utility by moving it from a manual runtime search to a >> > link-time certainty. >> > >> > My "big picture" problem is that Java?s evolution model currently >> > makes it difficult to "grow the language" via libraries that feel >> > native and are performan, such as the recent prototype exploration of >> > Float16. I believe the language should provide the infrastructure for >> > _Static Service Traits_ or otherwise make that kind of library-driven >> > growth a standard capability for all developers. >> > >> > I feel "corralled" into 1990s instance-based OOP. When I explore >> > data-oriented design or high-performance numeric abstractions, the >> > features found in my competitors' language tool belts would be >> > incredibly useful; without them, I find myself looking at alternate >> > language implementations just to avoid Java's structural obstacles. >> > >> > Given that Project Amber?s stated mission is to "right-size language >> > ceremony" and improve developer productivity, doesn't a proposal that >> > eliminates this Instantiation Tax and link-time service ceremony seem >> > like a relevant and worthy pursuit? >> > >> > -Steffen >> > >> > >> > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler >> wrote: >> >> >> >> The hardest part in designing and evolving a language is deciding >> which problems are important enough to merit a solution in the language and >> how their priorities compares to other problems. It?s the hardest part >> because the language team are expert at coming up with solutions, but they >> may not always know what problems people enoucnter in the field, how >> frequently they encounter them, and how they work around them today. >> >> >> >> I?m sure there is some problem hidden here and in your previous post, >> but it is not articulated well and is hidden in a poposed solution, even >> though no solution is even worth exploring before understanding the >> problem. And so the best way to get to a solution is for you to focus on >> the problem and only on the problem. >> >> >> >> What was the problem you *personally* ran into? How bad were its >> implications? How did you work around it? >> >> >> >> With the hard part done, the JDK team will then be able to assess its >> severity and think whether it merits a solution in the JDK, if so, where >> (language, libraries, or VM), and how to prioritise it against other >> problems worth tackling. Then they?ll be able to propose a solution, and >> that?s would be the time to try it out and discuss it. >> >> >> >> ? Ron >> >> >> >> >> >> >> >>> On 28 Jan 2026, at 00:28, Steffen Yount >> wrote: >> >>> >> >>> The recent thread "Java Language Enhancement: Disallow access to >> static members via object references" highlights a long-standing tension in >> Java's handling of static members. While that thread seeks to further >> decouple instance state from static logic, I would like to propose moving >> in the opposite direction: "doubling down" on Java?s compile-time and >> link-time static polymorphism. >> >>> >> >>> By beefing up java.util.ServiceLoader facilities and integrating its >> discovery mechanism directly into the language via Static Service Traits, >> we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's >> "growing the java language" presentation and the algebraic "well-known >> interface" model for custom numeric types (like Float16) proposed in Joe >> Darcy's "Paths to Support Additional Numeric Types on the Java Platform" >> presentation. >> >>> >> >>> == Static Service Traits for Java == >> >>> >> >>> I propose a system of Static Service Traits. I use the term "Trait" >> advisedly: this feature adopts a rigorous Coherence Model (inspired by >> systems like Rust) to ensure that service resolution is not merely a >> dynamic search, but a type-safe, deterministic binding of static >> capabilities to types. >> >>> 1. The service Contextual Keyword >> >>> We introduce service as a contextual modifier for interface >> declarations. Marking an interface as a service identifies it as a "service >> type" with a contract for static capabilities and a high-performance >> service provider registry. >> >>> >> >>> 2. Static Implementations and Extension Methods >> >>> ? Static Implementations: >> >>> ? In Interface Headers: interface MyTrait implements >> ServiceX. Methods are fulfilled as static. >> >>> ? In Class Headers: class MyClass implements static >> Numeric. Methods are implemented as static on the class. Existing >> signature rules prevent a method from being both a static and an instance >> implementation simultaneously. >> >>> ? Static Extension Methods: Desugared at the call site. >> myInstance.method() becomes MyClass.method(myInstance). Notably, if >> myInstance is null, it desugars to MyClass.method(null) without an >> immediate NullPointerException. >> >>> ? Ergonomic Aliases: To simplify signatures, we introduce private >> nested static type aliases This and Super (e.g., static This add(This a, >> This b)). >> >>> >> >>> 3. Operational Mechanics & Link-Time Integration >> >>> A ServiceLoader Controller is integrated into the JVM?s class-loading >> pipeline. During class definition, the Controller eagerly extracts all >> relevant metadata to populate the Static Service Provider Registry, >> including: >> >>> ? Header-level static implements and implements declarations. >> >>> ? Service binding descriptors from module-info.class. >> >>> ? META-INF/services/ provider-configuration files. >> >>> Hierarchical Precedence Resolution: To ensure deterministic binding, >> the Controller resolves call sites to their most specific service provider >> via a waterfall dispatch model: >> >>> ? Tier 1: Type Specialization: Most specific generic match wins, >> applying the same scrutiny and rules currently used for existing static >> overloaded method resolution. >> >>> ? Tier 2: Physical Locality: Provider in the same file >> (.jar/.class) as the caller wins. >> >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation >> path wins. >> >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > >> Transitive > Automatic. >> >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. >> >>> >> >>> 4. Coherence, The Orphan Rule, and Quarantining >> >>> To achieve the type-safety of a trait system, we enforce an adapted >> Orphan Rule: A module (or package on the classpath) must own either the >> service interface or the target type to define an implementation. >> >>> ? Coherence Enforcement: Violations in modular code trigger a >> LinkageError. >> >>> ? Behavioral Continuity: Violations in classpath code trigger a >> load-time warning and the provider is quarantined from the Static Registry. >> To ensure continuity, quarantined providers remain accessible via existing >> java.util.ServiceLoader API calls, protecting legacy iteration-based >> discovery while ensuring the integrity of the new link-time dispatch. >> >>> 5. Performance and AOT Considerations >> >>> This model transforms ServiceLoader into a link-time resolver. JIT >> compilers can treat service calls as direct invokestatic instructions, >> enabling aggressive optimization. This is highly compatible with Project >> Leyden and GraalVM, as precedence can be "baked" into the binary during AOT >> compilation. >> >>> Conclusion >> >>> By transitioning ServiceLoader to a link-time resolver, we provide a >> type-safe, high-performance path for algebraic types and witness-based >> generics. This allows Java to "grow" through libraries?fulfilling the goals >> of both Darcy and Goetz?while maintaining the performance and stability >> characteristics of the modern JVM. >> >>> >> >>> >> >>> Thoughts? >> >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Thu Jan 29 14:59:34 2026 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Thu, 29 Jan 2026 16:59:34 +0200 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: Presicely, you can, and this is what I said in the last few paragraphs. But, there are disadvantages, as I also wrote, and every time I write something like this I can't help but feel like I am monkey-patching. Strictly speaking, AP may not even start (as far as i know), if there are not annotations at all in the project, and doing things like discovering implantations in compile-time is the territory when work becomes a true monkey-patching, unless you require user to put annotations at least somewhere in the project, like on each package, so that their CU to check are discoverable. But this is a HUGE discoverability barrier, from new dependency to new annotation EVERYWHERE. One serious limitation of AP is that javac is single-threaded so there will always be synchronization points. You can get around this by creating static analyzer checks, but this is another lot of pain, as I'm sure you know: the only point when you see the feedback is the build, which often only happens in CI, and the best you may hope for in the meantime is the sanity of you static analyzer plugin's IDE support, but practice shows that even for something as widely adopted as spring modulith or nullaway, it takes years for major IDEs to adapt. And once again - static analyzer is not a part of the language. And, frankly speaking, AP also really feels like lurking in escape hatch. AP clearly may be the answer, but in it's current state, it lacks capabilities (remember Archie's checker framework adventure for example), UX and performance. But still, I don't think that interface contract validation should rely on something that is called ANNOTATION processor, as there are no annotation in this process, and you cant force your user to obey this contract without "gently asking" them to use you static analysis tools. But they very well may not On Thu, Jan 29, 2026, 14:54 Josiah Noel wrote: > Can't you write an annotation processor to do all three of those checks > during compilation? > > On Thu, Jan 29, 2026, 2:01?AM Olexandr Rotan > wrote: > >> I may be not exactly at the same topic as Steffen, but I think that, more >> vaguely speaking, the issue can be more generally described as "lack of >> compile time metaprogramming for contract verification", which manifests >> in, for example, such scenarios: >> >> 1. No way to verify jackson-using class has exactly one @JsonCreator >> 2. No way to assert there is exactly one static method that is invokable >> with given arguments (static traits specifically, for example ensure there >> is some unambigous metadata-producing method for runtime discovery >> mechanisms) >> 3. Or, more generally, no way to ensure that class A implements a >> word-phrased (on the contrary to method-phrased, which is statically >> enforced) contract of interface B, such as for SequencedCollection. Of >> course, this particular example is largely beyond the limits of >> metaprogramming, but for something simpler such as potential interface >> "UnmodifiableList" that would have declared that all modifying methods of >> List in class implementing UnmodifiableList should ultimately throw an >> exception or at least not mutate backing state fields, static checks are >> doable. >> >> The other thing is that this is what static analysis tools are made for. >> Unfortunately, not everyone uses even developer-provided analysis profiles >> when using said developer`s library, so the fact that there is no langauge >> level contract enforcement capabilities for more complex scenarios surely >> leads to a lot of unnecessary runtime errors. >> >> Though, of course, I admit that what I have described (metaprogrammatic >> contract enforcement) is a far larger scope then just static methods. >> Ultimately, even just static-method-contract enforcement would solve a lot >> of reflection-related issues for me when writing things like runtime >> discovery >> >> On Thu, Jan 29, 2026 at 2:31?AM Ron Pressler >> wrote: >> >>> The first two paragraphs boil down to ?it?s difficult to work with >>> user-defined numeric types, especially with a tight memory layout?. As you >>> probably know, this is a problem we?re already working on solving. >>> >>> Aside from that, I think the problem you?re reporting is: >>> >>> "The java.util.ServiceLoader ... requires explicit custom code to verify >>> bindings at startup for fail-fast behavior. The API remains an extra >>> invocation hurdle with its lookup and instantiation requirements.? >>> >>> What you?re saying about ?right-sizing? the language is absolutely >>> right, but for the JDK team to propose a solution to a problem you?ve >>> identified, the problem first needs to be understood. Assuming that what I >>> quoted is the problem you?ve tried expressing, can you elaborate more on: >>> >>> 1. Requires explicit custom code to verify bindings at startup for >>> fail-fast behavior. >>> >>> 2. The API remains an extra invocation hurdle with its lookup and >>> instantiation requirements. >>> >>> The more concrete you are, as in ?I tried to do X and this is the code I >>> wrote?, the easier it would be to understand. >>> I think I know what you mean in #1, but it would be helpful to show what >>> code you had to write. In #2, what is exactly the hurdle? E.g. let?s say >>> that the hurdle is that the API is verbose. That is only a problem if it >>> has to be used lots of times in a program, so what was your program that >>> required so many uses of ServiceLoader? >>> >>> (P.S. ?right-sizing? the language doesn?t mean that anything that could >>> be improved with a language feature should be because every addition to the >>> language complicates it. We like it when a non-trivial language feature can >>> solve a big problem or multiple small ones.) >>> >>> ? Ron >>> >>> >>> > On 28 Jan 2026, at 23:20, Steffen Yount >>> wrote: >>> > >>> > Hi Ron, >>> > >>> > Thank you for the feedback. It's totally reasonable to push for the >>> > "why" before getting into the "how." >>> > >>> > The personal problem I encounter is the severe and inconvenient >>> > extensibility costs of the current language model. When pursuing >>> > data-oriented design or domain-specific numeric types in Java, I find >>> > the language's current facilities to be a significant design obstacle. >>> > These are not just issues I find while building, but constraints that >>> > fundamentally alter how I consider a project's architecture before a >>> > single line of code is written. >>> > >>> > I have encountered the following specific friction points: >>> > >>> > The Instantiation Tax: On several occasions, I have turned to Java for >>> > intensive math calculations. These efforts typically start small but >>> > eventually outgrow Java's supported numeric abstractions. To move >>> > beyond small memory footprints and 64-bit representational limits, I >>> > find I must refactor to contiguous arrays, reusable Flyweight objects, >>> > and custom math methods just to manage the memory pressure and data >>> > type limitations. Because I cannot define polymorphic static contracts >>> > for these types, I am forced to pay an Instantiation Tax?maintaining >>> > "witness" objects just to access static logic. The ergonomic noise and >>> > heap-inefficiency of the unwanted object headers are so high that I am >>> > often discouraged from pursuing my original abstractions entirely. >>> > While I have used primitive long types as witnesses for static >>> > overloaded method binding, using the NewType pattern for type-safe >>> > bindings remains prohibitively expensive; a new NewType class's >>> > implementation immediately excludes it from participation within the >>> > language's built-in expression operators. In the aforementioned >>> > efforts, I have ultimately abandoned Java for C/C++ simply because >>> > they allowed me to shape the data layout and its static behavior >>> > without this "abstraction tax." >>> > >>> > The Expression Problem (Post-hoc Abstraction): When I find it >>> > necessary to treat third-party classes as part of a common >>> > abstraction?for instance, when attempting to resolve Guice-based >>> > injection or AOP design issues without the "magic" of runtime >>> > reflection?the traditional path is the Adapter Pattern. I find this >>> > route unsustainable; creating a new wrapper class for every instance >>> > not only fragments object identity but generates significant GC churn. >>> > I have seen production code with wrapper classes nested eight levels >>> > deep just to satisfy disparate abstractions. The ability to implement >>> > type level contracts rather than just instance level contracts, along >>> > with type level extension methods, would allow us to side-step the >>> > wrapper classes with implementations that bind to existing types >>> > without modifying their source code. The lack of them serves as a wall >>> > preventing me from designing the clean, type-safe, and AOT-friendly >>> > systems I know are possible elsewhere. >>> > >>> > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more >>> > of a library than a language feature. It requires explicit custom code >>> > to verify bindings at startup for fail-fast behavior. The API remains >>> > an extra invocation hurdle with its lookup and instantiation >>> > requirements. A coherent language-integrated, static service interface >>> > method dispatch and binding would dramatically reduce this ceremony >>> > and increase utility by moving it from a manual runtime search to a >>> > link-time certainty. >>> > >>> > My "big picture" problem is that Java?s evolution model currently >>> > makes it difficult to "grow the language" via libraries that feel >>> > native and are performan, such as the recent prototype exploration of >>> > Float16. I believe the language should provide the infrastructure for >>> > _Static Service Traits_ or otherwise make that kind of library-driven >>> > growth a standard capability for all developers. >>> > >>> > I feel "corralled" into 1990s instance-based OOP. When I explore >>> > data-oriented design or high-performance numeric abstractions, the >>> > features found in my competitors' language tool belts would be >>> > incredibly useful; without them, I find myself looking at alternate >>> > language implementations just to avoid Java's structural obstacles. >>> > >>> > Given that Project Amber?s stated mission is to "right-size language >>> > ceremony" and improve developer productivity, doesn't a proposal that >>> > eliminates this Instantiation Tax and link-time service ceremony seem >>> > like a relevant and worthy pursuit? >>> > >>> > -Steffen >>> > >>> > >>> > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler >>> wrote: >>> >> >>> >> The hardest part in designing and evolving a language is deciding >>> which problems are important enough to merit a solution in the language and >>> how their priorities compares to other problems. It?s the hardest part >>> because the language team are expert at coming up with solutions, but they >>> may not always know what problems people enoucnter in the field, how >>> frequently they encounter them, and how they work around them today. >>> >> >>> >> I?m sure there is some problem hidden here and in your previous post, >>> but it is not articulated well and is hidden in a poposed solution, even >>> though no solution is even worth exploring before understanding the >>> problem. And so the best way to get to a solution is for you to focus on >>> the problem and only on the problem. >>> >> >>> >> What was the problem you *personally* ran into? How bad were its >>> implications? How did you work around it? >>> >> >>> >> With the hard part done, the JDK team will then be able to assess its >>> severity and think whether it merits a solution in the JDK, if so, where >>> (language, libraries, or VM), and how to prioritise it against other >>> problems worth tackling. Then they?ll be able to propose a solution, and >>> that?s would be the time to try it out and discuss it. >>> >> >>> >> ? Ron >>> >> >>> >> >>> >> >>> >>> On 28 Jan 2026, at 00:28, Steffen Yount >>> wrote: >>> >>> >>> >>> The recent thread "Java Language Enhancement: Disallow access to >>> static members via object references" highlights a long-standing tension in >>> Java's handling of static members. While that thread seeks to further >>> decouple instance state from static logic, I would like to propose moving >>> in the opposite direction: "doubling down" on Java?s compile-time and >>> link-time static polymorphism. >>> >>> >>> >>> By beefing up java.util.ServiceLoader facilities and integrating its >>> discovery mechanism directly into the language via Static Service Traits, >>> we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's >>> "growing the java language" presentation and the algebraic "well-known >>> interface" model for custom numeric types (like Float16) proposed in Joe >>> Darcy's "Paths to Support Additional Numeric Types on the Java Platform" >>> presentation. >>> >>> >>> >>> == Static Service Traits for Java == >>> >>> >>> >>> I propose a system of Static Service Traits. I use the term "Trait" >>> advisedly: this feature adopts a rigorous Coherence Model (inspired by >>> systems like Rust) to ensure that service resolution is not merely a >>> dynamic search, but a type-safe, deterministic binding of static >>> capabilities to types. >>> >>> 1. The service Contextual Keyword >>> >>> We introduce service as a contextual modifier for interface >>> declarations. Marking an interface as a service identifies it as a "service >>> type" with a contract for static capabilities and a high-performance >>> service provider registry. >>> >>> >>> >>> 2. Static Implementations and Extension Methods >>> >>> ? Static Implementations: >>> >>> ? In Interface Headers: interface MyTrait implements >>> ServiceX. Methods are fulfilled as static. >>> >>> ? In Class Headers: class MyClass implements static >>> Numeric. Methods are implemented as static on the class. Existing >>> signature rules prevent a method from being both a static and an instance >>> implementation simultaneously. >>> >>> ? Static Extension Methods: Desugared at the call site. >>> myInstance.method() becomes MyClass.method(myInstance). Notably, if >>> myInstance is null, it desugars to MyClass.method(null) without an >>> immediate NullPointerException. >>> >>> ? Ergonomic Aliases: To simplify signatures, we introduce private >>> nested static type aliases This and Super (e.g., static This add(This a, >>> This b)). >>> >>> >>> >>> 3. Operational Mechanics & Link-Time Integration >>> >>> A ServiceLoader Controller is integrated into the JVM?s >>> class-loading pipeline. During class definition, the Controller eagerly >>> extracts all relevant metadata to populate the Static Service Provider >>> Registry, including: >>> >>> ? Header-level static implements and implements declarations. >>> >>> ? Service binding descriptors from module-info.class. >>> >>> ? META-INF/services/ provider-configuration files. >>> >>> Hierarchical Precedence Resolution: To ensure deterministic binding, >>> the Controller resolves call sites to their most specific service provider >>> via a waterfall dispatch model: >>> >>> ? Tier 1: Type Specialization: Most specific generic match wins, >>> applying the same scrutiny and rules currently used for existing static >>> overloaded method resolution. >>> >>> ? Tier 2: Physical Locality: Provider in the same file >>> (.jar/.class) as the caller wins. >>> >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation >>> path wins. >>> >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > >>> Transitive > Automatic. >>> >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. >>> >>> >>> >>> 4. Coherence, The Orphan Rule, and Quarantining >>> >>> To achieve the type-safety of a trait system, we enforce an adapted >>> Orphan Rule: A module (or package on the classpath) must own either the >>> service interface or the target type to define an implementation. >>> >>> ? Coherence Enforcement: Violations in modular code trigger a >>> LinkageError. >>> >>> ? Behavioral Continuity: Violations in classpath code trigger a >>> load-time warning and the provider is quarantined from the Static Registry. >>> To ensure continuity, quarantined providers remain accessible via existing >>> java.util.ServiceLoader API calls, protecting legacy iteration-based >>> discovery while ensuring the integrity of the new link-time dispatch. >>> >>> 5. Performance and AOT Considerations >>> >>> This model transforms ServiceLoader into a link-time resolver. JIT >>> compilers can treat service calls as direct invokestatic instructions, >>> enabling aggressive optimization. This is highly compatible with Project >>> Leyden and GraalVM, as precedence can be "baked" into the binary during AOT >>> compilation. >>> >>> Conclusion >>> >>> By transitioning ServiceLoader to a link-time resolver, we provide a >>> type-safe, high-performance path for algebraic types and witness-based >>> generics. This allows Java to "grow" through libraries?fulfilling the goals >>> of both Darcy and Goetz?while maintaining the performance and stability >>> characteristics of the modern JVM. >>> >>> >>> >>> >>> >>> Thoughts? >>> >> >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Thu Jan 29 15:06:38 2026 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Thu, 29 Jan 2026 17:06:38 +0200 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: And on the different note, annotations may not even be able to be put or AP may not be able to process some code, if you are integrating libraries/frameworks into your project. For example, if someone erroneously or not, wrote code with two constructors marked with @Inject/@Autowired, and you then register this type as bean in your DI container - you are bound to fail, no AP will even save you, as you cant "check" already compiled code with AP. You may try to use use-sote validation, but traversing over library class hierarchies in compile time may be a terrible experience, and some classes that library is compiled against may not be present in compilation process by be reachable in runtime, which is common case for libraries. So effectively, you just rely on the fact that lib dev obeyed contract, and cant force them to use the same static analysis as you, so your failure ultimately deferred to the runtime On Thu, Jan 29, 2026, 16:59 Olexandr Rotan wrote: > Presicely, you can, and this is what I said in the last few paragraphs. > But, there are disadvantages, as I also wrote, and every time I write > something like this I can't help but feel like I am monkey-patching. > > Strictly speaking, AP may not even start (as far as i know), if there are > not annotations at all in the project, and doing things like discovering > implantations in compile-time is the territory when work becomes a true > monkey-patching, unless you require user to put annotations at least > somewhere in the project, like on each package, so that their CU to check > are discoverable. But this is a HUGE discoverability barrier, from new > dependency to new annotation EVERYWHERE. > > One serious limitation of AP is that javac is single-threaded so there > will always be synchronization points. You can get around this by creating > static analyzer checks, but this is another lot of pain, as I'm sure you > know: the only point when you see the feedback is the build, which often > only happens in CI, and the best you may hope for in the meantime is the > sanity of you static analyzer plugin's IDE support, but practice shows that > even for something as widely adopted as spring modulith or nullaway, it > takes years for major IDEs to adapt. > > And once again - static analyzer is not a part of the language. And, > frankly speaking, AP also really feels like lurking in escape hatch. AP > clearly may be the answer, but in it's current state, it lacks capabilities > (remember Archie's checker framework adventure for example), UX and > performance. But still, I don't think that interface contract validation > should rely on something that is called ANNOTATION processor, as there are > no annotation in this process, and you cant force your user to obey this > contract without "gently asking" them to use you static analysis tools. But > they very well may not > > On Thu, Jan 29, 2026, 14:54 Josiah Noel wrote: > >> Can't you write an annotation processor to do all three of those checks >> during compilation? >> >> On Thu, Jan 29, 2026, 2:01?AM Olexandr Rotan >> wrote: >> >>> I may be not exactly at the same topic as Steffen, but I think that, >>> more vaguely speaking, the issue can be more generally described as "lack >>> of compile time metaprogramming for contract verification", which manifests >>> in, for example, such scenarios: >>> >>> 1. No way to verify jackson-using class has exactly one @JsonCreator >>> 2. No way to assert there is exactly one static method that is invokable >>> with given arguments (static traits specifically, for example ensure there >>> is some unambigous metadata-producing method for runtime discovery >>> mechanisms) >>> 3. Or, more generally, no way to ensure that class A implements a >>> word-phrased (on the contrary to method-phrased, which is statically >>> enforced) contract of interface B, such as for SequencedCollection. Of >>> course, this particular example is largely beyond the limits of >>> metaprogramming, but for something simpler such as potential interface >>> "UnmodifiableList" that would have declared that all modifying methods of >>> List in class implementing UnmodifiableList should ultimately throw an >>> exception or at least not mutate backing state fields, static checks are >>> doable. >>> >>> The other thing is that this is what static analysis tools are made for. >>> Unfortunately, not everyone uses even developer-provided analysis profiles >>> when using said developer`s library, so the fact that there is no langauge >>> level contract enforcement capabilities for more complex scenarios surely >>> leads to a lot of unnecessary runtime errors. >>> >>> Though, of course, I admit that what I have described (metaprogrammatic >>> contract enforcement) is a far larger scope then just static methods. >>> Ultimately, even just static-method-contract enforcement would solve a lot >>> of reflection-related issues for me when writing things like runtime >>> discovery >>> >>> On Thu, Jan 29, 2026 at 2:31?AM Ron Pressler >>> wrote: >>> >>>> The first two paragraphs boil down to ?it?s difficult to work with >>>> user-defined numeric types, especially with a tight memory layout?. As you >>>> probably know, this is a problem we?re already working on solving. >>>> >>>> Aside from that, I think the problem you?re reporting is: >>>> >>>> "The java.util.ServiceLoader ... requires explicit custom code to >>>> verify bindings at startup for fail-fast behavior. The API remains an extra >>>> invocation hurdle with its lookup and instantiation requirements.? >>>> >>>> What you?re saying about ?right-sizing? the language is absolutely >>>> right, but for the JDK team to propose a solution to a problem you?ve >>>> identified, the problem first needs to be understood. Assuming that what I >>>> quoted is the problem you?ve tried expressing, can you elaborate more on: >>>> >>>> 1. Requires explicit custom code to verify bindings at startup for >>>> fail-fast behavior. >>>> >>>> 2. The API remains an extra invocation hurdle with its lookup and >>>> instantiation requirements. >>>> >>>> The more concrete you are, as in ?I tried to do X and this is the code >>>> I wrote?, the easier it would be to understand. >>>> I think I know what you mean in #1, but it would be helpful to show >>>> what code you had to write. In #2, what is exactly the hurdle? E.g. let?s >>>> say that the hurdle is that the API is verbose. That is only a problem if >>>> it has to be used lots of times in a program, so what was your program that >>>> required so many uses of ServiceLoader? >>>> >>>> (P.S. ?right-sizing? the language doesn?t mean that anything that could >>>> be improved with a language feature should be because every addition to the >>>> language complicates it. We like it when a non-trivial language feature can >>>> solve a big problem or multiple small ones.) >>>> >>>> ? Ron >>>> >>>> >>>> > On 28 Jan 2026, at 23:20, Steffen Yount >>>> wrote: >>>> > >>>> > Hi Ron, >>>> > >>>> > Thank you for the feedback. It's totally reasonable to push for the >>>> > "why" before getting into the "how." >>>> > >>>> > The personal problem I encounter is the severe and inconvenient >>>> > extensibility costs of the current language model. When pursuing >>>> > data-oriented design or domain-specific numeric types in Java, I find >>>> > the language's current facilities to be a significant design obstacle. >>>> > These are not just issues I find while building, but constraints that >>>> > fundamentally alter how I consider a project's architecture before a >>>> > single line of code is written. >>>> > >>>> > I have encountered the following specific friction points: >>>> > >>>> > The Instantiation Tax: On several occasions, I have turned to Java for >>>> > intensive math calculations. These efforts typically start small but >>>> > eventually outgrow Java's supported numeric abstractions. To move >>>> > beyond small memory footprints and 64-bit representational limits, I >>>> > find I must refactor to contiguous arrays, reusable Flyweight objects, >>>> > and custom math methods just to manage the memory pressure and data >>>> > type limitations. Because I cannot define polymorphic static contracts >>>> > for these types, I am forced to pay an Instantiation Tax?maintaining >>>> > "witness" objects just to access static logic. The ergonomic noise and >>>> > heap-inefficiency of the unwanted object headers are so high that I am >>>> > often discouraged from pursuing my original abstractions entirely. >>>> > While I have used primitive long types as witnesses for static >>>> > overloaded method binding, using the NewType pattern for type-safe >>>> > bindings remains prohibitively expensive; a new NewType class's >>>> > implementation immediately excludes it from participation within the >>>> > language's built-in expression operators. In the aforementioned >>>> > efforts, I have ultimately abandoned Java for C/C++ simply because >>>> > they allowed me to shape the data layout and its static behavior >>>> > without this "abstraction tax." >>>> > >>>> > The Expression Problem (Post-hoc Abstraction): When I find it >>>> > necessary to treat third-party classes as part of a common >>>> > abstraction?for instance, when attempting to resolve Guice-based >>>> > injection or AOP design issues without the "magic" of runtime >>>> > reflection?the traditional path is the Adapter Pattern. I find this >>>> > route unsustainable; creating a new wrapper class for every instance >>>> > not only fragments object identity but generates significant GC churn. >>>> > I have seen production code with wrapper classes nested eight levels >>>> > deep just to satisfy disparate abstractions. The ability to implement >>>> > type level contracts rather than just instance level contracts, along >>>> > with type level extension methods, would allow us to side-step the >>>> > wrapper classes with implementations that bind to existing types >>>> > without modifying their source code. The lack of them serves as a wall >>>> > preventing me from designing the clean, type-safe, and AOT-friendly >>>> > systems I know are possible elsewhere. >>>> > >>>> > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more >>>> > of a library than a language feature. It requires explicit custom code >>>> > to verify bindings at startup for fail-fast behavior. The API remains >>>> > an extra invocation hurdle with its lookup and instantiation >>>> > requirements. A coherent language-integrated, static service interface >>>> > method dispatch and binding would dramatically reduce this ceremony >>>> > and increase utility by moving it from a manual runtime search to a >>>> > link-time certainty. >>>> > >>>> > My "big picture" problem is that Java?s evolution model currently >>>> > makes it difficult to "grow the language" via libraries that feel >>>> > native and are performan, such as the recent prototype exploration of >>>> > Float16. I believe the language should provide the infrastructure for >>>> > _Static Service Traits_ or otherwise make that kind of library-driven >>>> > growth a standard capability for all developers. >>>> > >>>> > I feel "corralled" into 1990s instance-based OOP. When I explore >>>> > data-oriented design or high-performance numeric abstractions, the >>>> > features found in my competitors' language tool belts would be >>>> > incredibly useful; without them, I find myself looking at alternate >>>> > language implementations just to avoid Java's structural obstacles. >>>> > >>>> > Given that Project Amber?s stated mission is to "right-size language >>>> > ceremony" and improve developer productivity, doesn't a proposal that >>>> > eliminates this Instantiation Tax and link-time service ceremony seem >>>> > like a relevant and worthy pursuit? >>>> > >>>> > -Steffen >>>> > >>>> > >>>> > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler >>>> wrote: >>>> >> >>>> >> The hardest part in designing and evolving a language is deciding >>>> which problems are important enough to merit a solution in the language and >>>> how their priorities compares to other problems. It?s the hardest part >>>> because the language team are expert at coming up with solutions, but they >>>> may not always know what problems people enoucnter in the field, how >>>> frequently they encounter them, and how they work around them today. >>>> >> >>>> >> I?m sure there is some problem hidden here and in your previous >>>> post, but it is not articulated well and is hidden in a poposed solution, >>>> even though no solution is even worth exploring before understanding the >>>> problem. And so the best way to get to a solution is for you to focus on >>>> the problem and only on the problem. >>>> >> >>>> >> What was the problem you *personally* ran into? How bad were its >>>> implications? How did you work around it? >>>> >> >>>> >> With the hard part done, the JDK team will then be able to assess >>>> its severity and think whether it merits a solution in the JDK, if so, >>>> where (language, libraries, or VM), and how to prioritise it against other >>>> problems worth tackling. Then they?ll be able to propose a solution, and >>>> that?s would be the time to try it out and discuss it. >>>> >> >>>> >> ? Ron >>>> >> >>>> >> >>>> >> >>>> >>> On 28 Jan 2026, at 00:28, Steffen Yount >>>> wrote: >>>> >>> >>>> >>> The recent thread "Java Language Enhancement: Disallow access to >>>> static members via object references" highlights a long-standing tension in >>>> Java's handling of static members. While that thread seeks to further >>>> decouple instance state from static logic, I would like to propose moving >>>> in the opposite direction: "doubling down" on Java?s compile-time and >>>> link-time static polymorphism. >>>> >>> >>>> >>> By beefing up java.util.ServiceLoader facilities and integrating >>>> its discovery mechanism directly into the language via Static Service >>>> Traits, we can facilitate the "Witness Object" paradigm discussed by Brian >>>> Goetz's "growing the java language" presentation and the algebraic >>>> "well-known interface" model for custom numeric types (like Float16) >>>> proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the >>>> Java Platform" presentation. >>>> >>> >>>> >>> == Static Service Traits for Java == >>>> >>> >>>> >>> I propose a system of Static Service Traits. I use the term "Trait" >>>> advisedly: this feature adopts a rigorous Coherence Model (inspired by >>>> systems like Rust) to ensure that service resolution is not merely a >>>> dynamic search, but a type-safe, deterministic binding of static >>>> capabilities to types. >>>> >>> 1. The service Contextual Keyword >>>> >>> We introduce service as a contextual modifier for interface >>>> declarations. Marking an interface as a service identifies it as a "service >>>> type" with a contract for static capabilities and a high-performance >>>> service provider registry. >>>> >>> >>>> >>> 2. Static Implementations and Extension Methods >>>> >>> ? Static Implementations: >>>> >>> ? In Interface Headers: interface MyTrait implements >>>> ServiceX. Methods are fulfilled as static. >>>> >>> ? In Class Headers: class MyClass implements static >>>> Numeric. Methods are implemented as static on the class. Existing >>>> signature rules prevent a method from being both a static and an instance >>>> implementation simultaneously. >>>> >>> ? Static Extension Methods: Desugared at the call site. >>>> myInstance.method() becomes MyClass.method(myInstance). Notably, if >>>> myInstance is null, it desugars to MyClass.method(null) without an >>>> immediate NullPointerException. >>>> >>> ? Ergonomic Aliases: To simplify signatures, we introduce >>>> private nested static type aliases This and Super (e.g., static This >>>> add(This a, This b)). >>>> >>> >>>> >>> 3. Operational Mechanics & Link-Time Integration >>>> >>> A ServiceLoader Controller is integrated into the JVM?s >>>> class-loading pipeline. During class definition, the Controller eagerly >>>> extracts all relevant metadata to populate the Static Service Provider >>>> Registry, including: >>>> >>> ? Header-level static implements and implements declarations. >>>> >>> ? Service binding descriptors from module-info.class. >>>> >>> ? META-INF/services/ provider-configuration files. >>>> >>> Hierarchical Precedence Resolution: To ensure deterministic >>>> binding, the Controller resolves call sites to their most specific service >>>> provider via a waterfall dispatch model: >>>> >>> ? Tier 1: Type Specialization: Most specific generic match wins, >>>> applying the same scrutiny and rules currently used for existing static >>>> overloaded method resolution. >>>> >>> ? Tier 2: Physical Locality: Provider in the same file >>>> (.jar/.class) as the caller wins. >>>> >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the >>>> delegation path wins. >>>> >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > >>>> Transitive > Automatic. >>>> >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath >>>> order. >>>> >>> >>>> >>> 4. Coherence, The Orphan Rule, and Quarantining >>>> >>> To achieve the type-safety of a trait system, we enforce an adapted >>>> Orphan Rule: A module (or package on the classpath) must own either the >>>> service interface or the target type to define an implementation. >>>> >>> ? Coherence Enforcement: Violations in modular code trigger a >>>> LinkageError. >>>> >>> ? Behavioral Continuity: Violations in classpath code trigger a >>>> load-time warning and the provider is quarantined from the Static Registry. >>>> To ensure continuity, quarantined providers remain accessible via existing >>>> java.util.ServiceLoader API calls, protecting legacy iteration-based >>>> discovery while ensuring the integrity of the new link-time dispatch. >>>> >>> 5. Performance and AOT Considerations >>>> >>> This model transforms ServiceLoader into a link-time resolver. JIT >>>> compilers can treat service calls as direct invokestatic instructions, >>>> enabling aggressive optimization. This is highly compatible with Project >>>> Leyden and GraalVM, as precedence can be "baked" into the binary during AOT >>>> compilation. >>>> >>> Conclusion >>>> >>> By transitioning ServiceLoader to a link-time resolver, we provide >>>> a type-safe, high-performance path for algebraic types and witness-based >>>> generics. This allows Java to "grow" through libraries?fulfilling the goals >>>> of both Darcy and Goetz?while maintaining the performance and stability >>>> characteristics of the modern JVM. >>>> >>> >>>> >>> >>>> >>> Thoughts? >>>> >> >>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From steffenyount at gmail.com Fri Jan 30 19:58:42 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Fri, 30 Jan 2026 11:58:42 -0800 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: Hi Ron, Thank you for the push to be concrete. After reflecting on your feedback and the Project Amber Charter, I realize that my previous attempt to provide acute examples obscured the "chronic" problem that animates me, that I'm encountering in the field, and that's motivating my request for assistance. The problem I encounter?every time there is a planning meeting for a new service deployment?is the increasing preference for non-Java toolchains like Rust, Go, and even TypeScript. The "workaround" for Java's perceived shortcomings today is simply that Java is not being selected for greenfield service development. It is excluded from the solution space in favor of languages that offer more rigorous type systems and compile-time type-safety guarantees, performance, zero-cost abstractions, more sophisticated data-oriented modeling, or easier domain model object interoperability. For me, this "workaround" creates a permanent maintenance tax. I am forced to be a polyglot programmer, navigating the disparate footguns and off-hours support escalations of fragmented distributed systems. These environments could have been a unified, high-performance Java ecosystem if the language facilities had kept pace with the performance, expressiveness, and safety found in our competitors' toolbelts. I interpreted Project Amber?s existence as proof positive that the JDK team already acknowledged the importance of these chronic issues. While I have deep respect for the monumental effort required of you personally to bring features like Virtual Threads from Quasar fibers to fruition?essentially a full-time job of justification, advocacy, and implementation?the process tax is lamentable. It shouldn't be so hard for outsider concerns to be considered. If every proposal must pass through an "acute use-case" litmus test before it is even discussed, the JDK team risks myopic outcomes that ignore the broader "relevance crisis" Java is facing today in a polyglot world. It's not my intent to step on the language team's toes here; I recognize that you are experts at crafting robust, long-term solutions. My description of "Static Service Traits" is not meant to be prescriptive, but rather illustrative of a possible, self-contained language extension. I think it represents a "low-hanging fruit" approach to introducing powerful, functor-like capabilities and static contracts to the language. It is by no means a complete remedy, but rather intended as a set of exciting retroactively consistent baby-steps geared toward growing Java's utility and developer interest. The acute ceremony I described with ServiceLoader is merely a symptom. It remains a library-level escape hatch for a problem?polymorphic static dispatch and retroactive extension?that the language currently cannot express natively. Without these facilities, Java remains "corralled" in an instance-based model while the industry moves toward the zero-cost, static-binding models of our competitors. Crucially, providing a native path for static extension methods would finally offer a viable alternative to the "wrapper-hell" and fragmented identity inherent in the Adapter Pattern. I believe the types of features I've mentioned align directly with Amber?s goal to "right-size ceremony," but more importantly, they help to address the problem of Java becoming a "legacy-only" choice in the field. I'd welcome your comments on the team's interest in these types of language features and would like to hear about how they stack up in terms of priority against other features the team is actively pursuing to grow the language. -Steffen On Wed, Jan 28, 2026 at 4:31?PM Ron Pressler wrote: > > The first two paragraphs boil down to ?it?s difficult to work with user-defined numeric types, especially with a tight memory layout?. As you probably know, this is a problem we?re already working on solving. > > Aside from that, I think the problem you?re reporting is: > > "The java.util.ServiceLoader ... requires explicit custom code to verify bindings at startup for fail-fast behavior. The API remains an extra invocation hurdle with its lookup and instantiation requirements.? > > What you?re saying about ?right-sizing? the language is absolutely right, but for the JDK team to propose a solution to a problem you?ve identified, the problem first needs to be understood. Assuming that what I quoted is the problem you?ve tried expressing, can you elaborate more on: > > 1. Requires explicit custom code to verify bindings at startup for fail-fast behavior. > > 2. The API remains an extra invocation hurdle with its lookup and instantiation requirements. > > The more concrete you are, as in ?I tried to do X and this is the code I wrote?, the easier it would be to understand. > I think I know what you mean in #1, but it would be helpful to show what code you had to write. In #2, what is exactly the hurdle? E.g. let?s say that the hurdle is that the API is verbose. That is only a problem if it has to be used lots of times in a program, so what was your program that required so many uses of ServiceLoader? > > (P.S. ?right-sizing? the language doesn?t mean that anything that could be improved with a language feature should be because every addition to the language complicates it. We like it when a non-trivial language feature can solve a big problem or multiple small ones.) > > ? Ron > > > > On 28 Jan 2026, at 23:20, Steffen Yount wrote: > > > > Hi Ron, > > > > Thank you for the feedback. It's totally reasonable to push for the > > "why" before getting into the "how." > > > > The personal problem I encounter is the severe and inconvenient > > extensibility costs of the current language model. When pursuing > > data-oriented design or domain-specific numeric types in Java, I find > > the language's current facilities to be a significant design obstacle. > > These are not just issues I find while building, but constraints that > > fundamentally alter how I consider a project's architecture before a > > single line of code is written. > > > > I have encountered the following specific friction points: > > > > The Instantiation Tax: On several occasions, I have turned to Java for > > intensive math calculations. These efforts typically start small but > > eventually outgrow Java's supported numeric abstractions. To move > > beyond small memory footprints and 64-bit representational limits, I > > find I must refactor to contiguous arrays, reusable Flyweight objects, > > and custom math methods just to manage the memory pressure and data > > type limitations. Because I cannot define polymorphic static contracts > > for these types, I am forced to pay an Instantiation Tax?maintaining > > "witness" objects just to access static logic. The ergonomic noise and > > heap-inefficiency of the unwanted object headers are so high that I am > > often discouraged from pursuing my original abstractions entirely. > > While I have used primitive long types as witnesses for static > > overloaded method binding, using the NewType pattern for type-safe > > bindings remains prohibitively expensive; a new NewType class's > > implementation immediately excludes it from participation within the > > language's built-in expression operators. In the aforementioned > > efforts, I have ultimately abandoned Java for C/C++ simply because > > they allowed me to shape the data layout and its static behavior > > without this "abstraction tax." > > > > The Expression Problem (Post-hoc Abstraction): When I find it > > necessary to treat third-party classes as part of a common > > abstraction?for instance, when attempting to resolve Guice-based > > injection or AOP design issues without the "magic" of runtime > > reflection?the traditional path is the Adapter Pattern. I find this > > route unsustainable; creating a new wrapper class for every instance > > not only fragments object identity but generates significant GC churn. > > I have seen production code with wrapper classes nested eight levels > > deep just to satisfy disparate abstractions. The ability to implement > > type level contracts rather than just instance level contracts, along > > with type level extension methods, would allow us to side-step the > > wrapper classes with implementations that bind to existing types > > without modifying their source code. The lack of them serves as a wall > > preventing me from designing the clean, type-safe, and AOT-friendly > > systems I know are possible elsewhere. > > > > The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more > > of a library than a language feature. It requires explicit custom code > > to verify bindings at startup for fail-fast behavior. The API remains > > an extra invocation hurdle with its lookup and instantiation > > requirements. A coherent language-integrated, static service interface > > method dispatch and binding would dramatically reduce this ceremony > > and increase utility by moving it from a manual runtime search to a > > link-time certainty. > > > > My "big picture" problem is that Java?s evolution model currently > > makes it difficult to "grow the language" via libraries that feel > > native and are performan, such as the recent prototype exploration of > > Float16. I believe the language should provide the infrastructure for > > _Static Service Traits_ or otherwise make that kind of library-driven > > growth a standard capability for all developers. > > > > I feel "corralled" into 1990s instance-based OOP. When I explore > > data-oriented design or high-performance numeric abstractions, the > > features found in my competitors' language tool belts would be > > incredibly useful; without them, I find myself looking at alternate > > language implementations just to avoid Java's structural obstacles. > > > > Given that Project Amber?s stated mission is to "right-size language > > ceremony" and improve developer productivity, doesn't a proposal that > > eliminates this Instantiation Tax and link-time service ceremony seem > > like a relevant and worthy pursuit? > > > > -Steffen > > > > > > On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler wrote: > >> > >> The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. > >> > >> I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. > >> > >> What was the problem you *personally* ran into? How bad were its implications? How did you work around it? > >> > >> With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. > >> > >> ? Ron > >> > >> > >> > >>> On 28 Jan 2026, at 00:28, Steffen Yount wrote: > >>> > >>> The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. > >>> > >>> By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. > >>> > >>> == Static Service Traits for Java == > >>> > >>> I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. > >>> 1. The service Contextual Keyword > >>> We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. > >>> > >>> 2. Static Implementations and Extension Methods > >>> ? Static Implementations: > >>> ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. > >>> ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. > >>> ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. > >>> ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). > >>> > >>> 3. Operational Mechanics & Link-Time Integration > >>> A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: > >>> ? Header-level static implements and implements declarations. > >>> ? Service binding descriptors from module-info.class. > >>> ? META-INF/services/ provider-configuration files. > >>> Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: > >>> ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. > >>> ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. > >>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. > >>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. > >>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. > >>> > >>> 4. Coherence, The Orphan Rule, and Quarantining > >>> To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. > >>> ? Coherence Enforcement: Violations in modular code trigger a LinkageError. > >>> ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. > >>> 5. Performance and AOT Considerations > >>> This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. > >>> Conclusion > >>> By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. > >>> > >>> > >>> Thoughts? > >> > From rob.ross at gmail.com Fri Jan 30 20:00:35 2026 From: rob.ross at gmail.com (Rob Ross) Date: Fri, 30 Jan 2026 12:00:35 -0800 Subject: My experience with IO.print() and IO.println() In-Reply-To: References: Message-ID: I was this person for years, nay, decades. And it held me back. It might be related to my ADHD. But I would *strongly* suggest you try to train these types of students to become comfortable with temporarily not knowing something. Once I learned to do this, my ability to learn really experienced exponential growth. My mental model of this concept is the "forward reference" in programming. It's like an IOU to the compiler - "This is an important function and I promise I will it explain it to you in detail when you're ready, but for now trust me that it exists and does the thing it says it will do." Sometimes you can decrease the cognitive load on the learner by hiding complexity from them, as when one starts to learn about strings in C and a good teacher will provide a `typedef char* string`, or what has been done with IO.print. But still, learning to be comfortable with being uncomfortable is a critical life skill. - Rob On Tue, Nov 25, 2025 at 4:15?AM David Alayachew wrote: > Hello @amber-dev , > > For example, there are a certain "type" of students that have a brain that > REFUSES to accept new info until they understand ALL details of what's in > front of them. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Fri Jan 30 20:34:53 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Fri, 30 Jan 2026 20:34:53 +0000 Subject: =?utf-8?B?UmU6IFByb3Bvc2FsOiBTdGF0aWMgU2VydmljZSBUcmFpdHPigJRFbmhhbmNp?= =?utf-8?B?bmcgSmF2YeKAmXMgU3RhdGljIFBvbHltb3JwaGlzbSBhbmQgU2VydmljZUxv?= =?utf-8?Q?ader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: > On 30 Jan 2026, at 19:58, Steffen Yount wrote: > > Hi Ron, > > The problem I encounter?every time there is a planning meeting for a > new service deployment?is the increasing preference for non-Java > toolchains like Rust, Go, and even TypeScript. Note that, with the possible exception of TypeScript, the languages you mentioned are far less popular than Java today, and show no sign of closing the gap. I?m certainly not saying they should be ignored, and we obviously keep abreast of developments in the programming language space, but adopting a feature cannot be justfied *just* because a far less successful language has it. > > The acute ceremony I described with ServiceLoader is merely a symptom. Okay, but since that is the symptom you?ve chosen to focus on, can you please describe it? Maybe it can be addressed on its own, and maybe as part of a larger change that can cure multiple symptoms, but first we need to know what it is. The reason saying ?ceremony? isn?t enough is this: Suppose some operation in Java takes 20 lines instead of 2. If this operation is done once every 10K lines, then this 10x improvement would amount to a 0.2% benefit, while still incurring the cost of complicating the language. Such a vague description simply isn?t actionable, and to improve things we need actionable information. > It remains a library-level escape hatch for a problem?polymorphic > static dispatch and retroactive extension?that the language currently > cannot express natively. Instead of talking in the abstract about a missing mechanism, can you please detail the specific problem you?ve encountered and are suggesting we should solve, in a way that will allow us to determine how serious of a problem this is compared to other problems we need to solve? > Without these facilities, Java remains > "corralled" in an instance-based model while the industry moves toward > the zero-cost, static-binding models of our competitors. I don?t know what ?an instance-based model? is and Java?s ability to make some operations zero-cost matches or exceeds most of our competitors already. Obviously, there?s plenty of room for improvement, but talking in such an abstract way doesn?t help us improve anything. If you want to help us improve Java, show us the code you have to write today, and explain why it?s problematic and why it?s an important problem to prioritise. As Brian Goetz says, ?tell us something we don?t know?. We know what features TypeScript and Go and Zig and Julia and Elixir and Rust and ATS and Python and C++ and Nim have. We don?t know what problem *you* ran into and believe is serious enough for the platform to solve. ? Ron > > > On Wed, Jan 28, 2026 at 4:31?PM Ron Pressler wrote: >> >> The first two paragraphs boil down to ?it?s difficult to work with user-defined numeric types, especially with a tight memory layout?. As you probably know, this is a problem we?re already working on solving. >> >> Aside from that, I think the problem you?re reporting is: >> >> "The java.util.ServiceLoader ... requires explicit custom code to verify bindings at startup for fail-fast behavior. The API remains an extra invocation hurdle with its lookup and instantiation requirements.? >> >> What you?re saying about ?right-sizing? the language is absolutely right, but for the JDK team to propose a solution to a problem you?ve identified, the problem first needs to be understood. Assuming that what I quoted is the problem you?ve tried expressing, can you elaborate more on: >> >> 1. Requires explicit custom code to verify bindings at startup for fail-fast behavior. >> >> 2. The API remains an extra invocation hurdle with its lookup and instantiation requirements. >> >> The more concrete you are, as in ?I tried to do X and this is the code I wrote?, the easier it would be to understand. >> I think I know what you mean in #1, but it would be helpful to show what code you had to write. In #2, what is exactly the hurdle? E.g. let?s say that the hurdle is that the API is verbose. That is only a problem if it has to be used lots of times in a program, so what was your program that required so many uses of ServiceLoader? >> >> (P.S. ?right-sizing? the language doesn?t mean that anything that could be improved with a language feature should be because every addition to the language complicates it. We like it when a non-trivial language feature can solve a big problem or multiple small ones.) >> >> ? Ron >> >> >>> On 28 Jan 2026, at 23:20, Steffen Yount wrote: >>> >>> Hi Ron, >>> >>> Thank you for the feedback. It's totally reasonable to push for the >>> "why" before getting into the "how." >>> >>> The personal problem I encounter is the severe and inconvenient >>> extensibility costs of the current language model. When pursuing >>> data-oriented design or domain-specific numeric types in Java, I find >>> the language's current facilities to be a significant design obstacle. >>> These are not just issues I find while building, but constraints that >>> fundamentally alter how I consider a project's architecture before a >>> single line of code is written. >>> >>> I have encountered the following specific friction points: >>> >>> The Instantiation Tax: On several occasions, I have turned to Java for >>> intensive math calculations. These efforts typically start small but >>> eventually outgrow Java's supported numeric abstractions. To move >>> beyond small memory footprints and 64-bit representational limits, I >>> find I must refactor to contiguous arrays, reusable Flyweight objects, >>> and custom math methods just to manage the memory pressure and data >>> type limitations. Because I cannot define polymorphic static contracts >>> for these types, I am forced to pay an Instantiation Tax?maintaining >>> "witness" objects just to access static logic. The ergonomic noise and >>> heap-inefficiency of the unwanted object headers are so high that I am >>> often discouraged from pursuing my original abstractions entirely. >>> While I have used primitive long types as witnesses for static >>> overloaded method binding, using the NewType pattern for type-safe >>> bindings remains prohibitively expensive; a new NewType class's >>> implementation immediately excludes it from participation within the >>> language's built-in expression operators. In the aforementioned >>> efforts, I have ultimately abandoned Java for C/C++ simply because >>> they allowed me to shape the data layout and its static behavior >>> without this "abstraction tax." >>> >>> The Expression Problem (Post-hoc Abstraction): When I find it >>> necessary to treat third-party classes as part of a common >>> abstraction?for instance, when attempting to resolve Guice-based >>> injection or AOP design issues without the "magic" of runtime >>> reflection?the traditional path is the Adapter Pattern. I find this >>> route unsustainable; creating a new wrapper class for every instance >>> not only fragments object identity but generates significant GC churn. >>> I have seen production code with wrapper classes nested eight levels >>> deep just to satisfy disparate abstractions. The ability to implement >>> type level contracts rather than just instance level contracts, along >>> with type level extension methods, would allow us to side-step the >>> wrapper classes with implementations that bind to existing types >>> without modifying their source code. The lack of them serves as a wall >>> preventing me from designing the clean, type-safe, and AOT-friendly >>> systems I know are possible elsewhere. >>> >>> The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more >>> of a library than a language feature. It requires explicit custom code >>> to verify bindings at startup for fail-fast behavior. The API remains >>> an extra invocation hurdle with its lookup and instantiation >>> requirements. A coherent language-integrated, static service interface >>> method dispatch and binding would dramatically reduce this ceremony >>> and increase utility by moving it from a manual runtime search to a >>> link-time certainty. >>> >>> My "big picture" problem is that Java?s evolution model currently >>> makes it difficult to "grow the language" via libraries that feel >>> native and are performan, such as the recent prototype exploration of >>> Float16. I believe the language should provide the infrastructure for >>> _Static Service Traits_ or otherwise make that kind of library-driven >>> growth a standard capability for all developers. >>> >>> I feel "corralled" into 1990s instance-based OOP. When I explore >>> data-oriented design or high-performance numeric abstractions, the >>> features found in my competitors' language tool belts would be >>> incredibly useful; without them, I find myself looking at alternate >>> language implementations just to avoid Java's structural obstacles. >>> >>> Given that Project Amber?s stated mission is to "right-size language >>> ceremony" and improve developer productivity, doesn't a proposal that >>> eliminates this Instantiation Tax and link-time service ceremony seem >>> like a relevant and worthy pursuit? >>> >>> -Steffen >>> >>> >>> On Wed, Jan 28, 2026 at 7:45?AM Ron Pressler wrote: >>>> >>>> The hardest part in designing and evolving a language is deciding which problems are important enough to merit a solution in the language and how their priorities compares to other problems. It?s the hardest part because the language team are expert at coming up with solutions, but they may not always know what problems people enoucnter in the field, how frequently they encounter them, and how they work around them today. >>>> >>>> I?m sure there is some problem hidden here and in your previous post, but it is not articulated well and is hidden in a poposed solution, even though no solution is even worth exploring before understanding the problem. And so the best way to get to a solution is for you to focus on the problem and only on the problem. >>>> >>>> What was the problem you *personally* ran into? How bad were its implications? How did you work around it? >>>> >>>> With the hard part done, the JDK team will then be able to assess its severity and think whether it merits a solution in the JDK, if so, where (language, libraries, or VM), and how to prioritise it against other problems worth tackling. Then they?ll be able to propose a solution, and that?s would be the time to try it out and discuss it. >>>> >>>> ? Ron >>>> >>>> >>>> >>>>> On 28 Jan 2026, at 00:28, Steffen Yount wrote: >>>>> >>>>> The recent thread "Java Language Enhancement: Disallow access to static members via object references" highlights a long-standing tension in Java's handling of static members. While that thread seeks to further decouple instance state from static logic, I would like to propose moving in the opposite direction: "doubling down" on Java?s compile-time and link-time static polymorphism. >>>>> >>>>> By beefing up java.util.ServiceLoader facilities and integrating its discovery mechanism directly into the language via Static Service Traits, we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's "growing the java language" presentation and the algebraic "well-known interface" model for custom numeric types (like Float16) proposed in Joe Darcy's "Paths to Support Additional Numeric Types on the Java Platform" presentation. >>>>> >>>>> == Static Service Traits for Java == >>>>> >>>>> I propose a system of Static Service Traits. I use the term "Trait" advisedly: this feature adopts a rigorous Coherence Model (inspired by systems like Rust) to ensure that service resolution is not merely a dynamic search, but a type-safe, deterministic binding of static capabilities to types. >>>>> 1. The service Contextual Keyword >>>>> We introduce service as a contextual modifier for interface declarations. Marking an interface as a service identifies it as a "service type" with a contract for static capabilities and a high-performance service provider registry. >>>>> >>>>> 2. Static Implementations and Extension Methods >>>>> ? Static Implementations: >>>>> ? In Interface Headers: interface MyTrait implements ServiceX. Methods are fulfilled as static. >>>>> ? In Class Headers: class MyClass implements static Numeric. Methods are implemented as static on the class. Existing signature rules prevent a method from being both a static and an instance implementation simultaneously. >>>>> ? Static Extension Methods: Desugared at the call site. myInstance.method() becomes MyClass.method(myInstance). Notably, if myInstance is null, it desugars to MyClass.method(null) without an immediate NullPointerException. >>>>> ? Ergonomic Aliases: To simplify signatures, we introduce private nested static type aliases This and Super (e.g., static This add(This a, This b)). >>>>> >>>>> 3. Operational Mechanics & Link-Time Integration >>>>> A ServiceLoader Controller is integrated into the JVM?s class-loading pipeline. During class definition, the Controller eagerly extracts all relevant metadata to populate the Static Service Provider Registry, including: >>>>> ? Header-level static implements and implements declarations. >>>>> ? Service binding descriptors from module-info.class. >>>>> ? META-INF/services/ provider-configuration files. >>>>> Hierarchical Precedence Resolution: To ensure deterministic binding, the Controller resolves call sites to their most specific service provider via a waterfall dispatch model: >>>>> ? Tier 1: Type Specialization: Most specific generic match wins, applying the same scrutiny and rules currently used for existing static overloaded method resolution. >>>>> ? Tier 2: Physical Locality: Provider in the same file (.jar/.class) as the caller wins. >>>>> ? Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path wins. >>>>> ? Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive > Automatic. >>>>> ? Tier 5: Sequential Order: Final tie-breaker via Classpath order. >>>>> >>>>> 4. Coherence, The Orphan Rule, and Quarantining >>>>> To achieve the type-safety of a trait system, we enforce an adapted Orphan Rule: A module (or package on the classpath) must own either the service interface or the target type to define an implementation. >>>>> ? Coherence Enforcement: Violations in modular code trigger a LinkageError. >>>>> ? Behavioral Continuity: Violations in classpath code trigger a load-time warning and the provider is quarantined from the Static Registry. To ensure continuity, quarantined providers remain accessible via existing java.util.ServiceLoader API calls, protecting legacy iteration-based discovery while ensuring the integrity of the new link-time dispatch. >>>>> 5. Performance and AOT Considerations >>>>> This model transforms ServiceLoader into a link-time resolver. JIT compilers can treat service calls as direct invokestatic instructions, enabling aggressive optimization. This is highly compatible with Project Leyden and GraalVM, as precedence can be "baked" into the binary during AOT compilation. >>>>> Conclusion >>>>> By transitioning ServiceLoader to a link-time resolver, we provide a type-safe, high-performance path for algebraic types and witness-based generics. This allows Java to "grow" through libraries?fulfilling the goals of both Darcy and Goetz?while maintaining the performance and stability characteristics of the modern JVM. >>>>> >>>>> >>>>> Thoughts? >>>> >> From ron.pressler at oracle.com Fri Jan 30 21:05:37 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Fri, 30 Jan 2026 21:05:37 +0000 Subject: =?utf-8?B?UmU6IFByb3Bvc2FsOiBTdGF0aWMgU2VydmljZSBUcmFpdHPigJRFbmhhbmNp?= =?utf-8?B?bmcgSmF2YeKAmXMgU3RhdGljIFBvbHltb3JwaGlzbSBhbmQgU2VydmljZUxv?= =?utf-8?Q?ader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> Message-ID: <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> P.S. Your previous suggestion about turning some warning into an error suffered the same flaw. All it said was that the pattern is potentially confusing. But that?s why there?s a warning. That?s not how to motivate turning a warning into an error. What is the problem with the warning? Is it that your team don?t turn it on? Do you turn it on but ignore it? Why is it that particular warning that merits becoming an error? Are the problems caused by it particularly serious? Without clearly stating what the problem you wish to solve is, it?s hard to judge the merit of any solution. If you look at our JEPs, they start with a motivation section. That?s the first step: clearly identify a problem and explain why it?s an important one for the platform to solve. It?s very important for us to know what problems people encounter, and we appreciate such reports, but if you don?t tell us exactly what the problem is we can?t help. ? Ron > On 30 Jan 2026, at 20:34, Ron Pressler wrote: > > > >> On 30 Jan 2026, at 19:58, Steffen Yount wrote: >> >> Hi Ron, >> >> The problem I encounter?every time there is a planning meeting for a >> new service deployment?is the increasing preference for non-Java >> toolchains like Rust, Go, and even TypeScript. > > > Note that, with the possible exception of TypeScript, the languages you mentioned are far less popular than Java today, and show no sign of closing the gap. I?m certainly not saying they should be ignored, and we obviously keep abreast of developments in the programming language space, but adopting a feature cannot be justfied *just* because a far less successful language has it. > >> >> The acute ceremony I described with ServiceLoader is merely a symptom. > > Okay, but since that is the symptom you?ve chosen to focus on, can you please describe it? Maybe it can be addressed on its own, and maybe as part of a larger change that can cure multiple symptoms, but first we need to know what it is. > > The reason saying ?ceremony? isn?t enough is this: Suppose some operation in Java takes 20 lines instead of 2. If this operation is done once every 10K lines, then this 10x improvement would amount to a 0.2% benefit, while still incurring the cost of complicating the language. > > Such a vague description simply isn?t actionable, and to improve things we need actionable information. > >> It remains a library-level escape hatch for a problem?polymorphic >> static dispatch and retroactive extension?that the language currently >> cannot express natively. > > > Instead of talking in the abstract about a missing mechanism, can you please detail the specific problem you?ve encountered and are suggesting we should solve, in a way that will allow us to determine how serious of a problem this is compared to other problems we need to solve? > >> Without these facilities, Java remains >> "corralled" in an instance-based model while the industry moves toward >> the zero-cost, static-binding models of our competitors. > > > I don?t know what ?an instance-based model? is and Java?s ability to make some operations zero-cost matches or exceeds most of our competitors already. Obviously, there?s plenty of room for improvement, but talking in such an abstract way doesn?t help us improve anything. > > If you want to help us improve Java, show us the code you have to write today, and explain why it?s problematic and why it?s an important problem to prioritise. As Brian Goetz says, ?tell us something we don?t know?. We know what features TypeScript and Go and Zig and Julia and Elixir and Rust and ATS and Python and C++ and Nim have. We don?t know what problem *you* ran into and believe is serious enough for the platform to solve. > > ? Ron From steffenyount at gmail.com Fri Jan 30 22:20:30 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Fri, 30 Jan 2026 14:20:30 -0800 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> Message-ID: For what it's worth, the recent "Java Language Enhancement: Disallow access to static members via object references" thread and suggestions there weren't mine. My first and otherwise most recent foray into engaging with you guys on this list was way back in late 2023. On Fri, Jan 30, 2026 at 1:05?PM Ron Pressler wrote: > > P.S. > > Your previous suggestion about turning some warning into an error suffered the same flaw. All it said was that the pattern is potentially confusing. But that?s why there?s a warning. That?s not how to motivate turning a warning into an error. What is the problem with the warning? Is it that your team don?t turn it on? Do you turn it on but ignore it? Why is it that particular warning that merits becoming an error? Are the problems caused by it particularly serious? > > Without clearly stating what the problem you wish to solve is, it?s hard to judge the merit of any solution. > > If you look at our JEPs, they start with a motivation section. That?s the first step: clearly identify a problem and explain why it?s an important one for the platform to solve. > > It?s very important for us to know what problems people encounter, and we appreciate such reports, but if you don?t tell us exactly what the problem is we can?t help. > > ? Ron > > > On 30 Jan 2026, at 20:34, Ron Pressler wrote: > > > > > > > >> On 30 Jan 2026, at 19:58, Steffen Yount wrote: > >> > >> Hi Ron, > >> > >> The problem I encounter?every time there is a planning meeting for a > >> new service deployment?is the increasing preference for non-Java > >> toolchains like Rust, Go, and even TypeScript. > > > > > > Note that, with the possible exception of TypeScript, the languages you mentioned are far less popular than Java today, and show no sign of closing the gap. I?m certainly not saying they should be ignored, and we obviously keep abreast of developments in the programming language space, but adopting a feature cannot be justfied *just* because a far less successful language has it. > > > >> > >> The acute ceremony I described with ServiceLoader is merely a symptom. > > > > Okay, but since that is the symptom you?ve chosen to focus on, can you please describe it? Maybe it can be addressed on its own, and maybe as part of a larger change that can cure multiple symptoms, but first we need to know what it is. > > > > The reason saying ?ceremony? isn?t enough is this: Suppose some operation in Java takes 20 lines instead of 2. If this operation is done once every 10K lines, then this 10x improvement would amount to a 0.2% benefit, while still incurring the cost of complicating the language. > > > > Such a vague description simply isn?t actionable, and to improve things we need actionable information. > > > >> It remains a library-level escape hatch for a problem?polymorphic > >> static dispatch and retroactive extension?that the language currently > >> cannot express natively. > > > > > > Instead of talking in the abstract about a missing mechanism, can you please detail the specific problem you?ve encountered and are suggesting we should solve, in a way that will allow us to determine how serious of a problem this is compared to other problems we need to solve? > > > >> Without these facilities, Java remains > >> "corralled" in an instance-based model while the industry moves toward > >> the zero-cost, static-binding models of our competitors. > > > > > > I don?t know what ?an instance-based model? is and Java?s ability to make some operations zero-cost matches or exceeds most of our competitors already. Obviously, there?s plenty of room for improvement, but talking in such an abstract way doesn?t help us improve anything. > > > > If you want to help us improve Java, show us the code you have to write today, and explain why it?s problematic and why it?s an important problem to prioritise. As Brian Goetz says, ?tell us something we don?t know?. We know what features TypeScript and Go and Zig and Julia and Elixir and Rust and ATS and Python and C++ and Nim have. We don?t know what problem *you* ran into and believe is serious enough for the platform to solve. > > > > ? Ron From ron.pressler at oracle.com Fri Jan 30 23:22:22 2026 From: ron.pressler at oracle.com (Ron Pressler) Date: Fri, 30 Jan 2026 23:22:22 +0000 Subject: =?utf-8?B?UmU6IFByb3Bvc2FsOiBTdGF0aWMgU2VydmljZSBUcmFpdHPigJRFbmhhbmNp?= =?utf-8?B?bmcgSmF2YeKAmXMgU3RhdGljIFBvbHltb3JwaGlzbSBhbmQgU2VydmljZUxv?= =?utf-8?Q?ader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> Message-ID: <923F3EB4-AF6A-4C5D-9709-8EF1193A842C@oracle.com> Oh, I apologise! > On 30 Jan 2026, at 22:20, Steffen Yount wrote: > > For what it's worth, the recent "Java Language Enhancement: Disallow > access to static members via object references" thread and suggestions > there weren't mine. My first and otherwise most recent foray into > engaging with you guys on this list was way back in late 2023. > > On Fri, Jan 30, 2026 at 1:05?PM Ron Pressler wrote: >> >> P.S. >> >> Your previous suggestion about turning some warning into an error suffered the same flaw. All it said was that the pattern is potentially confusing. But that?s why there?s a warning. That?s not how to motivate turning a warning into an error. What is the problem with the warning? Is it that your team don?t turn it on? Do you turn it on but ignore it? Why is it that particular warning that merits becoming an error? Are the problems caused by it particularly serious? >> >> Without clearly stating what the problem you wish to solve is, it?s hard to judge the merit of any solution. >> >> If you look at our JEPs, they start with a motivation section. That?s the first step: clearly identify a problem and explain why it?s an important one for the platform to solve. >> >> It?s very important for us to know what problems people encounter, and we appreciate such reports, but if you don?t tell us exactly what the problem is we can?t help. >> >> ? Ron From steffenyount at gmail.com Fri Jan 30 23:47:08 2026 From: steffenyount at gmail.com (Steffen Yount) Date: Fri, 30 Jan 2026 15:47:08 -0800 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: <923F3EB4-AF6A-4C5D-9709-8EF1193A842C@oracle.com> References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> <923F3EB4-AF6A-4C5D-9709-8EF1193A842C@oracle.com> Message-ID: no problem. On Fri, Jan 30, 2026 at 3:22?PM Ron Pressler wrote: > > Oh, I apologise! > > > On 30 Jan 2026, at 22:20, Steffen Yount wrote: > > > > For what it's worth, the recent "Java Language Enhancement: Disallow > > access to static members via object references" thread and suggestions > > there weren't mine. My first and otherwise most recent foray into > > engaging with you guys on this list was way back in late 2023. > > > > On Fri, Jan 30, 2026 at 1:05?PM Ron Pressler wrote: > >> > >> P.S. > >> > >> Your previous suggestion about turning some warning into an error suffered the same flaw. All it said was that the pattern is potentially confusing. But that?s why there?s a warning. That?s not how to motivate turning a warning into an error. What is the problem with the warning? Is it that your team don?t turn it on? Do you turn it on but ignore it? Why is it that particular warning that merits becoming an error? Are the problems caused by it particularly serious? > >> > >> Without clearly stating what the problem you wish to solve is, it?s hard to judge the merit of any solution. > >> > >> If you look at our JEPs, they start with a motivation section. That?s the first step: clearly identify a problem and explain why it?s an important one for the platform to solve. > >> > >> It?s very important for us to know what problems people encounter, and we appreciate such reports, but if you don?t tell us exactly what the problem is we can?t help. > >> > >> ? Ron From ethan at mccue.dev Sat Jan 31 18:01:44 2026 From: ethan at mccue.dev (Ethan McCue) Date: Sat, 31 Jan 2026 13:01:44 -0500 Subject: =?UTF-8?Q?Re=3A_Proposal=3A_Static_Service_Traits=E2=80=94Enhancing_Java?= =?UTF-8?Q?=E2=80=99s_Static_Polymorphism_and_ServiceLoader_Facilities?= In-Reply-To: References: <0539FB1E-6963-423B-A425-985CF9001437@oracle.com> <5ABA6449-89A9-4920-B640-8F39D7857263@oracle.com> <8793BA4A-C442-4136-B9D4-45A74F6F18C3@oracle.com> <923F3EB4-AF6A-4C5D-9709-8EF1193A842C@oracle.com> Message-ID: To give what (I think) is more the kind of feedback that would be helpful: I've been playing around with this library - github.com/bowbahdoe/java-type-classes. Finer points aside, it seems comparable to the planned language feature. What I am struggling with is when I would actually **want** to register witnesses for a typeclass. Take comparator for instance. If I am making a Point class I can register a witness for Comparator. public record Point(int x, int y) { @TypeClass.Witness public static Comparator comparator() { return Comparator.comparingInt(Point::x).thenComparingInt(Point::y); } } And then I can summon that witness later import com.garciat.typeclasses.TypeClasses; import com.garciat.typeclasses.api.Ty; void main() { var points = new ArrayList(); points.add(new Point((int) (Math.random() * 15), (int) (Math.random() * 15))); points.add(new Point((int) (Math.random() * 15), (int) (Math.random() * 15))); points.add(new Point((int) (Math.random() * 15), (int) (Math.random() * 15))); IO.println(points); points.sort(TypeClasses.witness(new Ty>() {})); IO.println(points); } But, I am already referencing the Point class directly. Syntax aside, I'm not sure why I would bother interacting with typeclasses at all when I can just write Point.comparator(); or (for this specific interface) making it implement Comparable. Really if I am touching comparators at all I will end up writing things like Point.comparingX(); and Point.comparingY(); On Fri, Jan 30, 2026 at 7:01?PM Steffen Yount wrote: > no problem. > > On Fri, Jan 30, 2026 at 3:22?PM Ron Pressler > wrote: > > > > Oh, I apologise! > > > > > On 30 Jan 2026, at 22:20, Steffen Yount > wrote: > > > > > > For what it's worth, the recent "Java Language Enhancement: Disallow > > > access to static members via object references" thread and suggestions > > > there weren't mine. My first and otherwise most recent foray into > > > engaging with you guys on this list was way back in late 2023. > > > > > > On Fri, Jan 30, 2026 at 1:05?PM Ron Pressler > wrote: > > >> > > >> P.S. > > >> > > >> Your previous suggestion about turning some warning into an error > suffered the same flaw. All it said was that the pattern is potentially > confusing. But that?s why there?s a warning. That?s not how to motivate > turning a warning into an error. What is the problem with the warning? Is > it that your team don?t turn it on? Do you turn it on but ignore it? Why is > it that particular warning that merits becoming an error? Are the problems > caused by it particularly serious? > > >> > > >> Without clearly stating what the problem you wish to solve is, it?s > hard to judge the merit of any solution. > > >> > > >> If you look at our JEPs, they start with a motivation section. That?s > the first step: clearly identify a problem and explain why it?s an > important one for the platform to solve. > > >> > > >> It?s very important for us to know what problems people encounter, > and we appreciate such reports, but if you don?t tell us exactly what the > problem is we can?t help. > > >> > > >> ? Ron > -------------- next part -------------- An HTML attachment was scrubbed... URL: From a.gegg at btinternet.com Tue Jan 27 11:44:53 2026 From: a.gegg at btinternet.com (Andy Gegg) Date: Tue, 27 Jan 2026 11:44:53 +0000 Subject: JEP 468 updating non-updatable fields In-Reply-To: References: <512e9c0f-56d0-40cf-87d1-ec86a997165a@btinternet.com> Message-ID: Hello, Thank you to all those who have spent time on this - I feel as if I poked something that needed to be poked. I know I talked about database ids which was a bad choice - as Brian has said, they shouldn't really be there! The point is that Records are lovely in so many ways that we're going to use them where they're *almost* right - and withers will make that easier and more appealing.? But because records have no setters there's no validation of the *change* except what the constructor can do - and that's intended for use where its parameter values have to be taken as correct (usually derived from a database, say), it cannot check a *change* is legal as it has no knowledge of previous values.? And so business logic starts to be implemented in front end code (in the with block), which is a Bad Thing. Thank you again for your time, Andy Gegg On 25/01/2026 19:09, Brian Goetz wrote: > The important mental model here is that a reconstruction (`with`) > expression is "just" a syntactic optimization for: > > ?- destructure with the canonical deconstruction pattern > ?- mutate the components > ?- reconstruct with the primary constructor > > So the root problem here is not the reconstruction expression; if you > can bork up your application state with a reconstruction expression, > you can bork it up without one. > > Primary constructors can enforce invariants _on_ or _between_ > components, such as: > > ? ? record Rational(int num, int denom) { > ? ? ? ? Rational { if (denom == 0) throw ... } > ? ? } > > or > > ? ? record Range(int lo, int hi) { > ? ? ? ? Range { if (lo > hi) throw... } > ? ? } > > What they can't do is express invariants between the record / carrier > state and "the rest of the system", because they are supposed to be > simple data carriers, not serialized references to some external > system. A class that models a database row in this way is complecting > entity state with an external entity id.? By modeling in this way, you > have explicitly declared that > > ? ? rec with { dbId++ } > > *is explicitly OK* in your system; that the components of the record > can be freely combined in any way (modulo enforced cross-component > invariants).? And there are systems in which this is fine!? But you're > imagining (correctly) that this modeling technique will be used in > systems in which this is not fine. > > The main challenge here is that developers will be so attracted to the > syntactic concision that they will willfully ignore the semantic > inconsistencies they are creating. > > > > > On 1/25/2026 1:37 PM, Andy Gegg wrote: >> Hello, >> I apologise for coming late to the party here - Records have been of >> limited use to me but Mr Goetz's email on carrier classes is >> something that would be very useful so I've been thinking about the >> consequences. >> >> Since? carrier classes and records are for data, in a database >> application somewhere or other you're going to get database ids in >> records: >> record MyRec(int dbId, String name,...) >> >> While everything is immutable this is fine but JEP 468 opens up the >> possibility of mutation: >> >> MyRec rec?= readDatabase(...); >> rec = rec with {name="...";}; >> writeDatabase(rec); >> >> which is absolutely fine and what an application wants to do. But: >> MyRec rec = readDatabase(...); >> rec = rec with {dbId++;}; >> writeDatabase(rec); >> >> is disastrous.? There's no way the canonical constructor invoked from >> 'with' can detect stupidity nor can whatever the database access >> layer does. >> >> In the old days, the lack of a 'setter' would usually prevent stupid >> code - the above could be achieved, obviously, but the code is >> devious enough to make people stop and think (one hopes). >> >> Here there is nothing to say "do not update this!!!" except code >> comments, JavaDoc and naming conventions. >> >> It's not always obvious which fields may or may not be changed in the >> application. >> >> record MyRec(int dbId, int fatherId,...) >> probably doesn't want >> rec = rec with { fatherId = ... } >> >> but a HR application will need to be able to do: >> >> record MyRec(int dbId, int departmentId, ...); >> ... >> rec = rec with { departmentId = newDept; }; >> >> Clearly, people can always write stupid code (guilty...) and the >> current state of play obviously allows the possibility (rec = new >> MyRec(rec.dbId++, ...);) which is enough to stop people using records >> here but carrier classes will be very tempting and that brings >> derived creation back to the fore. >> >> It's not just database ids which might need restricting from update, >> e.g. timestamps (which are better done in the database layer) and no >> doubt different applications will have their own business case >> restrictions. >> >> Thank you for your time, >> Andy Gegg >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: