PROPOSAL: Named method parameters

Paul Martin paul.martin at gmail.com
Tue Apr 21 11:32:13 PDT 2009


Hi,

Thanks for your reply.

(I've repeated the following request here, as well as at the end of my
reply, since I would really appreciate any suggestions!)

It would be very helpful/interesting to me if anyone could suggest
workable alternatives to solve my immediate problem of creating
immutable objects with required (and named) parameters (in Java code
and/or Spring-style config).

On Tue, Apr 21, 2009 at 4:36 PM, Joseph D. Darcy <Joe.Darcy at sun.com> wrote:
> Catching up on responses, as has been further discussed on the list
> recently, requests in this general vein have been considered before in JDK
> 6.  While there are certainly use-cases this facility could clarify, I
> remain unconvinced of the cost/benefit ratio of this proposal.

I'm currently using Spring to write a threaded application, and
believe that named method parameters could help in the following ways:

1. Creation of immutable objects by allowing parameter names to be
specified in Java method calls

It is good practice to have immutable objects where possible (or at
least to reduce the mutability of objects) in concurrent applications
(which avoids many publication and synchronization issues, as well as
reducing the program state space).  This means that member variables
of Immutable objects must be marked as final, and so can only be
assigned by the constructor.  There are a number of alternatives:

a. Pass parameter values to the constructor without parameter names.
This is ok when there are very few parameters or they have very
distinct and meaningful types, but not so good otherwise.  It is true
that the IDE can highlight the parameter names (usually as a pop-up or
mouse-over-hover), but I think that it would be easy to miss errors.
Comments after each value could help, but there would be no guarantee
that they are ever correct or that they stay in sync with code
refactoring.

b. Use the Builder pattern (as in Effective Java item #2), which does
allow immutable objects to be created with (effectively) named
parameters, but has the following disadvantages:

- It is more verbose (even in the simplified version that sets builder
members directly in a subclass without using setters)

- More importantly: there is no guarantee that all of the members are
set (without a lot of additional validation code in the Builder) -
effectively all of the members become optional.  If a developer
forgets to set a property in the builder, then the built object will
just get the default value (though conversely, builders are therefore
a reasonable way of getting default parameters).

This also affects refactoring - if a member is added, then it would be
difficult to ensure that all builders were updated to set a value.

Note that it may be possible to enhance analysis tools (such as
Findbugs) to detect cases where not all of a Builder's methods have
been called (perhaps in conjunction with an annotation), but that may
also be impossible in many cases (such as where the Builder is passed
between methods).

c. I also think that this use of the Builder pattern is not really in
the spirit of the GoF book, which states (in its Applicability
section):

(begin quote)
Use the Builder pattern when

    * the algorithm for creating a complex object should be
independent of the parts that make up the object and how they're
assembled.

    * the construction process must allow different representations
for the object that's constructed.
(end quote)

Neither of these are involved in the case where a Builder is just
being used to simulate named parameters (to aid
comprehension/documentation).


To summarise, my requirement for named parameters in Java code is
motivated by the fact that I don't think that there is currently a
nice way to create immutable objects such that the parameter/property
names can be easily seen in the code and that ensures that all
required parameters/properties are specified.

2. Adding parameter names to the classfiles/reflection system for
dynamic object creation

(This section is similar to Jean-Baptiste Bugeaud's proposal, and is
effectively the same as bug 6444738)

This is motivated by my use of Spring, but I think that it is
applicable to other technologies (and maybe even other JVM languages).

Currently in Spring you can define beans by specifying constructor
argument values (anonymously), using and/or property values (via named
setters).  This has the same disadvantages to that shown in section 1:
- Use anonymous constructor parameters, but then the code is harder to
understand and maintain.
- Use properties, which means that your bean cannot be immutable.
- Use a Builder, which involves additional code.  Note that Spring can
check that @Required properties have been set (and also doesn't have
first-class support for Builders, but could be extended to do so).

The same kind of problems occur as in 1, though in this case the
parameter names would need to be available at runtime, for Spring to
interrogate.

The result of this is that (from my experience) most Spring beans are
unnecessarily mutable, since it is unnecessarily difficult to do
otherwise.


There are probably other benefits, but those are the ones that
motivated my proposal; I just feel that the alternatives are
unnecessarily difficult.  What do you do in similar situations?

> A few design notes, a single @PublicParameterNames annotation with a boolean
> member could be used instead of a pair of "do it/don't do it" annotations.

That is a better idea!  How would this sit with the rule against
annotations affecting program flow?  Would it count here, because the
annotation is only causing additional metadata to be generated by the
compiler?

> A full proposal for this facility would need to deal with the cases where
> the number of parameters in the class file differs from the number of
> parameters in the source code, a situation already mildly exposed when
> calling toGenericString on some Method and Constructor objects.

Yes, my proposal didn't have the required level of detail.  If it were
to proceed, then experience from projects such as Paranamer might
help.

> Not supporting parameter reordering greatly simplifies the proposal by
> leaving method selection largely unaffected, but also seems to limit the
> usefulness of the proposal.  If the main benefit is just seeing the
> parameter name in the program text; can't the IDE do that?
>

The goal of my proposal was just to get the simplest implementation of
named parameters that I thought would work and be useful.  Therefore I
explicitly discounted:
- Parameter reordering - I could only see disadvantages (from the
references at the start of my proposal), without a major benefit.
- Default parameters - This would be a large change to method
resolution, and I thought that it could be addressed independently of
named parameters.  I also think that it is something that can be
already achieved with overloaded methods and/or Builders, whereas
named parameters let us do something new (readable creation of
immutable objects).

> I'd isn't clear to me how @PublicParameterNames would interact with
> overridden methods in class hierarchies.  Presumably the names of the static
> type of the call site would need be match, even though the runtime type
> could differ.  If a subclass overrode a method and changed its parameter
> names (which currently has no affect on compatibility), the subclass would
> no longer be substitutable for the parent class, which would break a
> fundamental design principle.

I guess that could be argued either way:

- Specifying @PublicParameterNames means that overridden methods must
keep the same parameter names, or

- overridden methods can change the parameter names (with the static
type of the call site used as you suggested).

I think that at least the first alternative could be argued for
(though that would limit applicability to only new classes), since
changing the names of getters and setters will break callers, and
named parameters could be considered to be similar to those.

> At least there is no binary compatibility impact since the names would only
> be checked at compile time and binary compatibility is about preserving the
> ability to link.

Yes, that was one of my main goals.


Is that helpful?

It seems that there wasn't a lot of enthusiasm for my proposal (or
Reinier's and Jean-Battiste's alternatives), but I think that there is
some elsewhere (as shown by the blogs that I referenced, and the fact
that it was at least considered for JDK 6).

It would be very helpful/interesting to me if anyone could suggest
workable alternatives to solve my immediate problem of creating
immutable objects with required (and named) parameters (in Java code
and/or Spring-style config).

Regards,

Paul

>
> -Joe
>
> Paul Martin wrote:
>>
>> 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