I was very surprised when I read Adam Bien's blog post on named parameters in Java 8.
Named parameters? Well... not quite. At least not the kind of named parameters we know from languages such as Visual Basic, Groovy and many others. But it turned out to be an interesting read nonetheless.
Java 8 API
Java 8 introduces another feature to the reflection API. You can read the names of the parameters of a method. Have you ever seen those annoying synthetic parameter names arg0, arg1, arg2 in the debugger? That's what happens if parameter names get lost. Granted, the average programmer will hardly ever want to read the name of a parameter via reflection. That's just not your frame of mind when you implement business logic. But it's attractive for framework programmers, so application programmers will benefit from the feature on the long run.
Take Spring's @RequestParam as an example. They introduced the annotation basically because they couldn't access the parameter name:
@Controller @RequestMapping("/pets") @SessionAttributes("pet") public class EditPetForm { @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) {cited from the Spring MVC documentation
The annotation @RequestParam simply repeats the parameter name. In theory, you could choose another @RequestParam, but why should you? Java 8 makes the annotation superfluous. Read the parameter name, and you're done.
Reading the parameter names is as simple as can be:
Method m = MyFavoriteClass.class.getMethods()[0]; Parameter[] parameters = m.getParameters(); for (Parameter parameter : parameters) { System.out.println(parameter.getName() + " "); }The Java 8 compiler switch
However, parameter names aren't compiled into the byte code by default. That's for a good reason: the extra information requires more memory, and it might confuse older tools dealing with the byte code. Hence you have to add a compiler switch to enable named parameters:
javac -parametersIf you omit the compiler switch, you can still call parameter.getName()
, but the calls yields synthetic names such as arg0, arg1, and arg2.
What about earlier Java versions?
Now for the interesting stuff. When I researched the article, I learned parameter names can be read for a long time. That shouldn't come as a surprise: every debugger shows the real parameter names. However, it's not possible using the official reflection API. I always thought debuggers somehow access the source code to get additional information, but there's another way. If you compile a Java class with the default settings additional debug info is added to the class files. The parameters of a method are the first few variables of the variable table. Their names can be read using a byte code library such as ASM or BCEL:
com.sun.org.apache.bcel.internal.classfile.Method bcelMethod = Repository.lookupClass(method.getDeclaringClass()).getMethod(method); LocalVariableTable localVariableTable = bcelMethod.getLocalVariableTable(); LocalVariable localVariable = localVariableTable.getLocalVariable(0); if (localVariable.getStartPC() == 0) { System.out.println(localVariable.getName()); }Inspired by via Java-Reflection Methoden-parameter-Bezeichnung ermitteln (in German)
Spring
Spring offers a built-in class to accesss parameter names:
found at StackOverflow.com
The DefaultParameterNameDiscoverer uses the Java 8 API, or a down-stripped version ASM in older versions of Java.
There's also an alternative implementation of ParameterNameDiscoverer in Spring dealing with AspectJ: AspectJAdviceParameterNameDiscoverer. This seems to be a sophisticated class that's rarely used (correct me if I'm wrong!). In most cases, the default implementation does the trick, too.
Alternative solutions that don't depend on the debug compile switch
Paranamer adds an additional compilation step that store the parameter names in synthetic static methods of your class. The advantage of this approach is it also works without the debug compiler switch.
Java's annotation processor API (which allows you to write your own preprocessor) gives you access to parameter names. APT is part of the compilation process, so it doesn't have to rely on the information in the class file but can access data stored in the AST during compilation.
Wrapping it up
Java 8 introduces a surprisingly simple mechanism to read method parameter names via reflection. This could be great news to framework developers, but it's spoiled by the decision not to activate the feature by default. As it turns out, you don't need the new Java 8 feature at all. The vast majority or Java byte code includes the debug information (because it's invaluable when it comes to tracing an error in a production stack trace). So it's possible to read parameter names using ASM or BCEL. Maybe - just maybe - framework developers come up with interesting solutions if the word spreads.
Adam Bien's blog post on named parameters in Java 8
Oracle's documentation on Java 8 reflection enhancements
How to get parameter names in Java 8
Read parameter names via reflection with Java 8 (in German)
Read parameter name with ASM or BCEL (in German)