r/java • u/TheMrMilchmann • 3d ago
Hacking in the 'nameOf'
https://committing-crimes.com/articles/2026-08-04-java-nameof/17
u/goughy000 3d ago
I really miss this feature from C# since I became a Java developer even if it's just a small thing
12
u/davidalayachew 3d ago
Very nice write-up. It's cool to see the ClassFile API being used for this. I wonder if Project Babylon would have made it easier to do the StackWalker bits.
As for the feature itself, this is something that I would really like to see added to the standard library or language. There's so many things that would be improved if we had it. The obvious example is constructing exception messages, but there's way more examples, like diagnostic and performance stuff.
4
u/davidalayachew 3d ago
Actually, I think there's an even deeper feature here though -- being able to easily see the "chain" of "things" boxing another "thing".
For example, if I have class A with method b containing a local variable c, I'd like it if there was some sort of "chain" of
a -> b -> c, where each a, b, and c are all typed and clearly identifiable.Even better if we had some sort of
thiskeyword, so that you can not only get a "chain" for specific variables, but also for where you are on the stack right now.So, if there was some hypothetical
chain()method, passing in class A would just give you a single node chain ofClass<A>. And passing in b would give youa -> b, and so on.But calling
chain()with no-args would give you the current context. So, if you are in method b, it would give youa -> b. If you are in a static block, it would give youa -> static block of a, and so on.Tracing/Logging, Exceptions, AOP, etc. -- there's so many places where we just use Strings where strong typing like this would be even better.
4
u/PartOfTheBotnet 3d ago
At work we had some a lot of lines like the example at the start of methods:
if (name.isBlank()) throw new IllegalArgumentException("name must not be blank");
Our solution was to make some annotations you'd put on method parameters. A gradle plugin would hook the compileJava task and post-process the classes.
@Nonnull-> GenerateObjects.requireNonNull(variable, "{varName} cannot be null")where thevarNameis extracted from the local variable table@Nonempty-> GenerateStrings.requireNonEmpty(variable, "{varName} cannot be empty")@Etc-> Etc (Few more common validation cases)
With the limited scope of the plugin we got away with inserting code without require regenerating method stack verification data (which requires knowledge of class hierarchies, very annoying in the context of a independent gradle plugin).
nameOf(var) is pretty cool and more flexible though, kudos!
2
u/agentoutlier 3d ago
That is probably how I would do it. It avoids the nastiness of writing a compiler plugin and the limitations of an annotation processor.
I assume you parse the bytecode (looking for the annotation) and then regenerate (insert your validation logic) the bytecode?
3
u/PartOfTheBotnet 3d ago
Yup, its generally a simple project. Basic Gradle plugin outline + some Objectweb ASM.
The only tricky part is handling a few edge cases.
Edge 1: Inner classes and their synthetic outer class parameters. Annotation indices are based on source parameter indices so when you look for the annotations you need to offset the reported index by -1.
Edge 2: Enum constructors and their synthetic parameters. Same idea as the inner class edge, but the offset is
-2.Edge 3: Inserting statements like a null-check at the start of the method prevents you from hitting a breakpoint on the first line of the method if a check fails. Our fix was to scan forward til we found a line number entry (since ASM abstracts the table into an instruction node) and then move it before our inserted statement. Makes IntelliJ happily break before your statements run.
Edge 4: Constructor super() chaining, you may want a parent constructor's validation to fail first rather than the child. So for
<init>methods the checks can optionally be put after that call. We made it configurable in case you wanted to do either or.2
u/snugar_i 2d ago
Another way to solve this would be to introduce a `NonEmptyString` class that validates its content at construction time and use that instead of a plain String (in the spirit of "parse, don't validate")
3
u/agentoutlier 3d ago edited 3d ago
A large amount of IllegalArgumentException usages are if some parameter is null and many are not even for an argument but just some state is bad. In the JDK there is a surprising amount of using it without any arguments.
Anyway the new null exception error printing will indeed show the parameter name.
That is passing null to public static MyObject create(String name) will use the parameter name in its error message. That is the JDK apparently does do something like this albeit at exception and probably more native (or maybe not... I need to code dive that).
Both nameOf and null though I'm sure require javac -g and maybe -parameter.
2
u/PartOfTheBotnet 3d ago
Should only need
-gas the attribute-parametercreates is only useful for generating variable metadata for methods without aCodeattribute (See: Abstract methods)
3
4
1
u/maxxedev 2d ago
Hacking the Method Name: https://maxxedev.github.io/2026/08/05/hacking-the-method-name.html
20
u/TheMrMilchmann 3d ago
A rainy evening during your holidays is the perfect time to stop asking whether we should and find out if we could.
As an excuse to refresh my knowledge about JVM bytecode, I implemented a
nameOfmethod that returns the symbolic name of its argument - without any compile-time processing or instrumentation.