PROPOSAL: Named and Optional parameters, "Java-style"
Brian Goetz
brian.goetz at oracle.com
Thu Aug 18 09:19:42 PDT 2011
Paul is right, that classfile changes definitely push something out of
the "small" bucket.
However, "parameter names in class files" is a long-standing request
from both the enterprise and IDE crowds, and one that might possibly
show up in a future version of Java, and if it did, there would be a
place to record various parameter-specific information.
But, even if it did, another thing that makes a proposal "big" is
"touching method overload resolution rules". And the essence of this
proposal is adding another layer of complexity to method overload
resolution.
On 8/18/2011 11:06 AM, Paul Benedict wrote:
> I believe recording the name of argument methods would require a class
> file update. That might be against the spirit of a "coin" enhancement.
>
> 2011/8/18 Frédéric Martini<frederic.martini at gmail.com>:
>> Hello,
>>
>> (before to start, sorry for my poor english)
>>
>>
>> I'd like to propose the implementation of named& optional method
>> parameters, similar to the Java 5.0 varargs.
>>
>>
>>
>>
>> PROPOSAL: Named and Optional parameters, "Java-style"
>>
>> AUTHOR(S): Frédéric Martini
>>
>> OVERVIEW
>>
>> FEATURE SUMMARY:
>>
>> Named parameters free you from the need to remember or to look up the
>> order of parameters in the parameter lists of called methods. The
>> value for each parameter will be specified by name (instead of by
>> order).
>>
>>
>> Optional parameters allow methods to specity a default value for
>> parameters, that allow caller to omit theses. If no value is sent for
>> theses parameters, the default value is used.
>>
>>
>>
>> MAJOR ADVANTAGE:
>>
>> Generally, this is implemented via syntactic sugar and affects only
>> the compilation step. But this would make a lot of limitation in a
>> language like Java :
>>
>> * The widespread use of "Named parameter" will put the parameter
>> names in the method's signature. So any changes at this level could
>> potentially break compatibility.
>>
>> * The use of "Optional parameter" limits the method's evolution :
>> Adding an optional parameter causes incompatibilities (break at runtime).
>> Default values are limited to constant, and fixed at compile-time.
>> Their changes involve a recompilation of callers codes, and they can
>> not be modified by overriding...
>>
>> (See reference "Use Optional Parameters to Minimize Method Overloads"
>> for détail of these problems on C# implementation)
>>
>>
>> This proposal opts for another approach similar to the Java 5.0
>> varargs, which impacts both the compilation and runtime steps : all
>> "named& optional" parameters will be stored in a sort of map, an
>> retrieved or set to default-value at runtime .
>>
>>
>>
>>
>> MAJOR BENEFIT:
>>
>> Allows the creation of clearer and precise API.
>> They can easily replace most of "builder pattern" and method's
>> overloading, using a single method...
>>
>> Without any impact on existing code or method signature that don't use
>> "named& optional parameters".
>>
>>
>> MAJOR DISADVANTAGE:
>>
>> Method's call will be slowest when using named& optional args.
>>
>>
>>
>> ALTERNATIVES:
>>
>> Method's overloading and/or "builder pattern". But this is boring :(
>>
>>
>>
>>
>> EXAMPLES
>>
>> SIMPLE EXAMPLE:
>>
>> // Method declaration
>> // The named& optional parameters are distinguished by brackets {}
>>
>> public void method(int standardArg, {int namedArg, int namedAndOptionalArg=0}) {
>> System.out.printf("%d;%d;%d", standardArg, namedArg, namedAndOptionalArg);
>> }
>>
>>
>> // Method call
>> // The named& optional parameters MUST be called by name.
>>
>> method( 1, namedArg:2, namedAndOptionalArg=3 ); // output: 1;2;3
>> method( 1, namedAndOptionalArg=3, namedArg:2 ); // output: 1;2;3
>> method( 1, namedArg:2 ); // output: 1;2;0
>>
>> // Of course compiler will check that the call is correct :
>> // + Non-optional parameters MUST be present.
>> // + Invalid parameter's name will generate error.
>>
>> method( 1 ); // COMPIL ERROR : "namedArg" is required
>> method( 1, namedArg:2, invalidName:3 ); // COMPIL ERROR :
>> "invalidName" is not a valid name
>>
>>
>>
>>
>> ADVANCED EXAMPLE:
>>
>>
>> // Optional parameters can use contant OR statement :
>>
>> public void method({String separator=";", Locale
>> locale=Locale.getDefault(), String
>> lineSeparator=System.getProperty("line.separator")}) {
>>
>> }
>>
>>
>> // Method declaration can use varargs, that allow the usage of any
>> name/value without compile error
>> // Each implementation can check for theses parameters...
>>
>> public void method({String filename, ...} options);
>>
>> // all these call are valid :
>> method(filename:"file.txt");
>> method(filename:"file.txt", encoding:"utf-8");
>> method(filename:"file.txt", encoding:"utf-8", append:true);
>> method(filename:"file.txt", encoding:"utf-8", append:true, anything:anyValue);
>>
>>
>> DETAILS
>>
>> SPECIFICATION:
>>
>>
>> The use of Named& Optionnal paramters MUST be specified on the method
>> signature.
>> Named parameters will be distinguished by brackets {}, and MUST be
>> passed by name at call time.
>> Any named parameter can have a default value, that can be a
>> compile-time constant OR a statement.
>>
>>
>> // exemple01 : use of named parameter :
>>
>> public void exemple01({int foo, String bar}) {
>> System.out.printf("exemple01(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>> // exemple02 : use of named& optional parameter using constant :
>>
>> public void exemple02({int foo=30, String bar="hi"}) {
>> System.out.printf("exemple02(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>> // exemple03 : use of named& optional parameter using constant AND statement :
>>
>> public void exemple03({int foo=30, String bar=System.getProperty("os.name")}) {
>> System.out.printf("exemple03(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>>
>> // exemple04 : use of named& optional parameter using constant AND statement,
>> // AND use varargs to allow any name/value parameters.
>>
>> public void exemple04({int foo=30, String
>> bar=System.getProperty("os.name"), ...} args) {
>> System.out.printf("exemple04(foo=%d, bar=%s) + %s%n", foo, bar, args);
>> }
>>
>>
>> // Calling example :
>>
>> exemple01(foo:10, bar:"hello");
>>
>> System.out.println();
>>
>> exemple02(foo:10, bar:"hello");
>> exemple02(foo:10);
>> exemple02(bar:"hello");
>> exemple02();
>>
>> System.out.println();
>>
>> exemple03(foo:10, bar:"hello");
>> exemple03(foo:10);
>> exemple03(bar:"hello");
>> exemple03();
>>
>> System.out.println();
>>
>> exemple04(foo:10, bar:"hello", any=100, name="value");
>>
>>
>> COMPILATION:
>>
>> At compile time, each "named& optional" method will be converted to a
>> method using a parameters of type "OptArgs". The type is contains a
>> Map with all parameters name/value. Theses methods will be annoted to
>> describe parameters names/types, and a basic code willbe generated to
>> initialise parameters as local variables
>>
>>
>>
>>
>> // exemple01 : use of named parameter :
>>
>> @OptArgsInfo({
>> @OptArg(name="foo", type=int.class),
>> @OptArg(name="bar", type=String.class),
>> })
>> public void exemple01(OptArgs $generated$) {
>> final int foo = $generated$.retrieve("foo", int.class);
>> final String bar = $generated$.retrieve("bar", String.class);
>>
>> System.out.printf("exemple01(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>>
>> The annotation @OptArgsInfo will describe all parameters that the
>> method can receive in the "OptArgs" parameters.
>> The "OptArgs" object contains a retrieve(String,Class) method in order
>> to get the parameter value for the specified name/type.
>>
>>
>>
>>
>> // exemple02 : use of named& optional parameter using constant :
>>
>> @OptArgsInfo(value={
>> @OptArg(name="foo", type=int.class, optional=true),
>> @OptArg(name="bar", type=String.class, optional=true),
>> })
>> public void exemple02(OptArgs $generated$) {
>> final int foo = $generated$.retrieve("foo", int.class, 30);
>> final String bar = $generated$.retrieve("bar", String.class, "hi");
>>
>> System.out.printf("exemple02(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>>
>> Here the named parameters are optionals (because they have a default value).
>> If parameter is absent, the method retrieve(String,Class<T>,T) return
>> the default constant value.
>>
>>
>>
>>
>> // exemple03 : use of named& optional parameter using constant AND statement :
>>
>> @OptArgsInfo(value={
>> @OptArg(name="foo", type=int.class, optional=true),
>> @OptArg(name="bar", type=String.class, optional=true),
>> })
>> public void exemple03(OptArgs $generated$) {
>> final int foo = $generated$.retrieve("foo", int.class, 30);
>> final String bar = $generated$.contains("bar", String.class)
>> ? $generated$.retrieve("bar", String.class)
>> : System.getProperty("os.name");
>>
>> System.out.printf("exemple03(foo=%d, bar=%s)%n", foo, bar);
>> }
>>
>>
>> For optional parameters that use statement as default value, we use
>> the ternary operator "?" to correctly initialize the variable when
>> parameter is absent.
>>
>>
>>
>>
>>
>> // exemple04 : use of named& optional parameter using constant AND statement,
>> // AND use varargs to allow any name/value parameters.
>>
>> @OptArgsInfo(acceptUnknownArgs=true, value={
>> @OptArg(name="foo", type=int.class, optional=true),
>> @OptArg(name="bar", type=String.class, optional=true),
>> })
>> public void exemple04(OptArgs args) {
>> final int foo = args.retrieve("foo", int.class, 30);
>> final String bar = args.contains("bar", String.class)
>> ? args.retrieve("bar", String.class)
>> : System.getProperty("os.name");
>>
>> System.out.printf("exemple04(foo=%d, bar=%s) + %s%n", foo, bar, args);
>> }
>>
>>
>> The annotation contains the value acceptUnknownArgs=true in order to
>> inform the compiler that this method accept any name/value in addition
>> to its declared parameters. In the body of the method, it may be
>> possible to use the object "OptArg" in order to access to these
>> additional parameters...
>>
>>
>>
>>
>>
>> // Calling example :
>>
>>
>> exemple01(OptArgs.make("foo", 10, "bar", "hello"));
>>
>> System.out.println();
>>
>> exemple02(OptArgs.make("foo", 10, "bar", "hello"));
>> exemple02(OptArgs.make("foo", 10));
>> exemple02(OptArgs.make("bar", "hello"));
>> exemple02(OptArgs.empty());
>>
>> System.out.println();
>>
>> exemple03(OptArgs.make("foo", 10, "bar", "hello"));
>> exemple03(OptArgs.make("foo", 10));
>> exemple03(OptArgs.make("bar", "hello"));
>> exemple03(OptArgs.empty());
>>
>> System.out.println();
>>
>> exemple04(OptArgs.make("foo", 10, "bar", "hello", "any", 100, "name", "value"));
>>
>>
>>
>>
>>
>> This solution propose a less-disturbing choise :
>>
>>
>> * The "named and optional" parameters block must be at the end of the
>> method's parameter list (like varargs).
>> * Named parameters can only be used with specific method that
>> contains a "named& optional" block.
>> * There are no impact on method that don't use "named& optional parameters".
>> * We can override an "named and optional" method.
>> * We can add an optional parameter to an existant "named and
>> optional" method (or on an overrided method).
>> * We can remove an optional parameter on an overrided method (if
>> present it will be ignored)
>> * We can modify the default value of an optional parameter withour
>> having to recompile all the caller's code.
>> * Renaming a "named parameter", removing a default-value or adding a
>> non-optional parameter is an incompatible change.
>>
>>
>>
>> TESTING:
>>
>>
>> A basic implementation can be found here (source-code) :
>> http://adiguba.developpez.com/coin-optargs/
>>
>>
>> LIBRARY SUPPORT:
>>
>> No.
>>
>> REFLECTIVE APIS:
>>
>> java.lang.reflect.Method and java.lang.reflect.Constructor could be
>> enhanced with new methods that return informations about "named&
>> optional parameters" :
>>
>>
>> isOptArgs()
>> return true if method use "named& optional parameters"
>>
>> getOptArgs()
>> return an array of OptArg annotation, that describe the "named&
>> optional parameters".
>>
>> isOptArgsAcceptUnknownArgs()
>> return this if method accept unknown name/value args (varargs-like).
>>
>>
>> OTHER CHANGES:
>>
>> Javadoc must be updated in order to accept @param that match the named
>> parameters, and indicate optional parameter.
>>
>> MIGRATION:
>>
>> This is a new syntax for new method.
>>
>>
>> COMPATIBILITY
>>
>> BREAKING CHANGES:
>>
>> No (new syntax).
>>
>>
>> EXISTING PROGRAMS:
>>
>> The changes required by this feature should be transparent to existing
>> source and class files.
>> It only consist to a method with a new and specific type.
>>
>> Overriding is still possible.
>>
>> REFERENCES
>>
>> Use Optional Parameters to Minimize Method Overloads :
>> http://msdn.microsoft.com/en-us/vcsharp/ff467291
>>
>>
>> EXISTING BUGS:
>>
>> ?
>>
>> URL FOR PROTOTYPE :
>>
>> A basic implementation with demo that represent the code generated by
>> the compileur (I do not have sufficient expertise to offer a modified
>> version of javac) :
>> http://adiguba.developpez.com/coin-optargs/
>>
>>
>>
>>
>>
>>
>> Thanks for reading :)
>>
>>
>
More information about the coin-dev
mailing list