PROPOSAL: Named method parameters

Paul Martin paul.martin at gmail.com
Sat Mar 21 09:15:15 PDT 2009


I'd like to propose the implementation of optionally named method
parameters, and suggest that they are particularly useful when creating
immutable objects (which are important in our increasingly concurrent
world).

Note that this is a topic that seems to have been considered before (see the
note in http://paulhammant.com/blog/announcing_paranamer.html which suggests
that it was even considered for Java 6), but I can't see it in the current
list of Java 7 changes or in the Project Coin mailing list archives (though
others have also discussed it in relation to Java 7, such as Alex Miller in
http://tech.puredanger.com/2007/08/15/dr-java7/ and
http://tech.puredanger.com/2007/10/11/java7-roundup-32/).  Therefore I am
proposing it here - I think that it is an important change (unless anyone
can suggest how I can reasonably create an immutable object with four or
more properties without using a Builder or a list of unnamed parameters).

Also note that other relevant references include:
http://blogs.sun.com/abuckley/entry/named_parameters (which discusses why
parameter reordering might be difficult),
http://beust.com/weblog/archives/000096.html (which suggests that we do not
need named parameters, but really just shows the use of the Builder pattern
which has limitations).


Named method parameters

AUTHOR(S):

Paul Martin

OVERVIEW

FEATURE SUMMARY:

Named parameters allow a method or constructor caller to explicitly state
the parameter name of each parameter value passed to the method or
constructor.  Without named parameters, only the order of the parameter
values is significant (and it may not be clear what each value represents).

MAJOR ADVANTAGE:

This is a relatively simple way of introducing named parameters to the Java
platform.

MAJOR BENEFIT:

Named parameters allow method calls with relatively long lists of parameters
(greater than 3) to be understood by developers.  This can also facilitate
the direct use of constructors and factory methods to create immutable
objects, where otherwise Builders or mutable JavaBeans would be required.

MAJOR DISADVANTAGE:

The syntax for method calls would change to allow both named and unnamed
parameters to be used.

Named parameters might be used too often, which could unnecessarily clutter
source files.

Metadata to store the names of the parameters must be added to classfiles
(for example as annotations).

The use of annotations to describe named parameters would mean that no
metadata would be stored about the parameter names used by the method
caller, and so no runtime checks to match the parameter names being used
could be made (only compile-time).  If the parameter names change (but
otherwise the method signature does not), then the method caller would not
be aware of the changes until it was recompiled.  Note that this is existing
behaviour for unnamed parameters, and would only be a problem if the meaning
of the parameters also changes (which should indicate that the caller would
need to change anyway, but this would not be detected automatically at
runtime).

ALTERNATIVES:

Unnamed parameters can make the same method calls as named parameters, but
if there are many parameters they are harder to understand and prone to
ordering-related bugs (particularly for primitive types or Strings).

The Builder pattern can be used to simulate named parameters in
constructors/factory methods.  For example the following class uses a
Builder to initialise itself (note that only two properties are used to
simplify the example):

    public final class MyClass {

        private final String name;
        private final int age;

        private MyClass(Builder b) {
            this.name = b.name;
            this.age = b.age;
        }

        public static class Builder {

            private String name;
            private int age;

            /**
             * Name to set.
             */
            public Builder setName(String name) {
               this.name = name;
               return this;
            }

            /**
             * Age to set
            */
            public Builder setAge(int age) {
                this.age = age;
                return this;
            }

            public MyClass build() {
               return new MyClass(this);
            }
        }
    }

Objects can then be constructed in the following manner:

    MyClass obj = new MyClass.Builder().setName("Fred").setAge(53).build();

However, the use of a builder requires additional code, and all parameters
are effectively optional from the perspective of the compiler (it cannot
easily check that all of the required setter methods have been called), so
bugs may occur where parameter values are not set.  Note that runtime checks
can be made to ensure that all property values are set, but they involve
additional work and cannot be used by the compiler (though static analysis
of the code in conjunction with annotations might be able to repeat many of
these checks).

There are also many ways to implement named parameters that are different to
the solution proposed here.  These include:

Implement optional named parameters on all methods:  This avoids the need
for the @PublicParameterNames annotation, but may mean inconsistent usage of
named parameters - the annotation is also a suggestion for the use of named
parameters by the caller (which IDEs can also make use of).  This will also
highlight the difference to classfiles compiled with earlier versions of
Java, where named parameters cannot be used at all (since it will then not
be obvious when named parameters can and cannot be used).

Add parameter names to the classfile as 'fundamental' method metadata rather
than as annotations: This may allow closer integration of the named
parameters with the rest of the language, but its impact on the rest of the
language (and JVM) is much greater.  The same inconsistencies and
incompatibilities described in the previous alternative would also apply.
In addition, the use of annotations should not preclude the subsequent
modification of classfiles in a later release - the annotations could still
remain (at a potential cost of some duplication).

EXAMPLES

SIMPLE EXAMPLE:

Constructor declaration:

    @PublicParameterNames
    public MyClass(String name, int age) { ....

Constructor use:

    MyClass obj = new MyClass(name: "Fred", age: 53);

ADVANCED EXAMPLE: Show advanced usage(s) of the feature.

The full class definition from the simple example is shown below, as a
contrast to that provided in the Builder example.

    public final class MyClass {

        private final String name;
        private final int age;

        /**
         * @param name Name to set
         * @param age Age to set
         */

        @PublicParameterNames
        public MyClass(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

DETAILS

SPECIFICATION:

The @PublicParameterNames annotation is used on a constructor or method to
allow named parameters to be used when calling it.  It can also be applied
to a class or package to apply to all methods within.  Similarly, a
@NoParameterNames annotation can be used on a constructor, method, or class
to override the use of @PublicParameterNames at a higher level.

The compiler can then automatically generate a
@PublicParameterName(value="parameter-name") annotation for each affected
method.  These can then be used by the compiler (and IDE) to validate the
parameter names used by callers of the method or constructor.

The use of parameter names by callers is always optional (primarily for
backwards compatibility).  However, parameter names cannot be used for some
parameters and not others (this would make the code harder to read).

Parameters must always be specified in the same order regardless of whether
parameter names are used - parameter reordering by callers is not allowed
(reordering would make overloading difficult, and is not really necessary).

Inheritance: Neither the @PublicParameterNames annotation (and
@NoParameterNames annotation), or the names of the parameters themselves,
need to be inherited.  This is because only static evaluation of a caller's
parameter names will be applied (rather than at runtime), so the callers
parameter names only need to match those of the declared type of the
target.  However, it would still be advisable to reuse the same parameter
names and annotations when overriding a method (IDEs could help with this).

COMPILATION:

The compiler must automatically generate a
@PublicParameterName(value="parameter-name") annotation for each method and
constructor annotated with @PublicParameterNames (or whose the parent class
or package is annotated with @PublicParameterNames and the child class or
method does not override it with @NoParameterNames).

For example:

    int myMethod(int param1, String param2)

Would effectively become:

    int myMethod(@PublicParameterName("param1") int param1,
@PublicParameterName("param2") String param2)

Method-calling syntax would need to change, using a colon to separate the
parameter name and value where the are used.  For example:

   MyClass obj = new MyClass(name: "Fred", age: 53);

When a method caller uses parameter names, the compiler must check that
those parameter names exactly match the value of generated
@PublicParameterName annotations of the target method or constructor.  It is
a compilation error if they do not match or the target method or constructor
does not have named parameters.

IDEs can use the @PublicParameterNames and @NoParameterNames annotations (or
@PublicParameterName directly) to automatically insert the relevant
parameter names as part of code-completion.  Additional refactoring support
would also be required to enable parameter names to be renamed, or added and
removed completely.

Frameworks such as Spring can use the same annotations to allow objects to
be created from their configuration files using named parameters to
constructors and factory methods.  Currently they only allow objects to be
created from configuration files as JavaBeans (with setter methods) or with
ordered (and unnamed) parameters.

TESTING:

The feature can be tested by compiling and running programs that exercise
the feature.

LIBRARY SUPPORT:

No.

REFLECTIVE APIS:

java.lang.reflect.Method and java.lang.reflect.Constructor could be enhanced
with new methods that return parameter names.  However, that is not required
to successfully implement the proposal.

OTHER CHANGES:

No.  The use of named parameters is always optional.  The javadoc tool
already documents parameter names.  Other tools/libraries might benefit from
these changes, but their use is not required.

MIGRATION:

The @PublicParameterNames annotation (and @NoParameterNames annotation)
simply needs to be added to the appropriate methods.  IDEs could then
suggest the use of parameter names for the method's callers, and apply the
changes automatically.

COMPATIBILITY

BREAKING CHANGES:

No.

EXISTING PROGRAMS:

The changes required by this feature should be transparent to existing
source and class files if annotations are used internally to describe the
named parameters.  I am not sure what the impact would be otherwise.

REFERENCES

EXISTING BUGS:

4124331 (though it is set to 11-Closed, Will Not Fix, request for
enhancement, and from 1998)



More information about the coin-dev mailing list