Annotation Interface ParametersMustMatchByName


@Documented @Retention(CLASS) @Target({METHOD,CONSTRUCTOR,TYPE}) public @interface ParametersMustMatchByName
Methods and constructors annotated as such will ensure the call sites pass in expressions that "look like" matching the parameter names.

Usually, if your method or constructor has multiple parameters of the same type, it adds risk of them being passed in the wrong order, particularly if they are primitive types like strings or ints. You could create a builder, but builders carry significant boilerplate and you could forget to set a required parameter, resulting in runtime error. By simply annotating the constructor with @ParametersMustMatchByName, you get compile-time safety for free.

For example if you have a record like the following:


 @ParametersMustMatchByName
 record Profile(String userId, String userName) {}
 
You can construct it with

   new Profile(currentUser.getId(), currentUser.getName())
 
But it will fail to compile if the constructor were defined as record Profile(String userName, String userId). The currentUser.getId() expression matches the userId parameter name because the effective tokens of currentUser.getId() is ["current", "user", "id"] ("get" and "is" prefixes are ignored), which includes as a subsequence the ["user", "id"] tokens from userId.

If the argument expression is indeed as expected despite not matching the parameter name, you can always add an explicit comment to tell the compiler and the code readers that: "trust me, I know what I'm doing".

For example:

   new Dimension(/* width */ list.get(0), /* height */ list.get(1));
 
Or, trailing comments can also be used under the style of one-arg-per-line:

   new Dimension(
       list.get(0),  // width
       list.get(1)); // height
 

In a sense,

/* width */ list.get(0)
serves a similar purpose to .setWidth(list.get(0)) in a builder chain – they both explicitly spell out "width" as the target to ensure you don't pass in height by mistake. Except with a @ParametersMustMatchByName-annotated constructor, the per-parameter comment is on a need basis that's only necessary for code not already self-evident. If you have a width local variable for example, simply pass it in without the syntax redundancy mandated by builder.setWidth(width). The most concise code is also the safe code, guaranteed by the compile-time plugin.

For literal parameters (string literals, int literals, enum constants, class literals), the parameter name matching rule is relaxed if the corresponding method parameter's type is unique (no other parameters share the same type).

Note that method references used as functional interfaces are not checked for parameter name matching between the method declaration and the functional interface's method names.

To use, just ensure you have the following snippet in your pom.xml:


 <build>
   <pluginManagement>
     <plugins>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
           <annotationProcessorPaths>
             <path>
               <groupId>com.google.errorprone</groupId>
               <artifactId>error_prone_core</artifactId>
               <version>2.40.0</version>
             </path>
             <path>
               <groupId>com.google.mug</groupId>
               <artifactId>mug-errorprone</artifactId>
               <version>9.9.1</version>
             </path>
           </annotationProcessorPaths>
         </configuration>
       </plugin>
     </plugins>
   </pluginManagement>
 </build>
 
Since:
9.9.1