PROPOSAL: Named and Optional parameters, "Java-style"
Frédéric Martini
frederic.martini at gmail.com
Thu Aug 18 05:11:40 PDT 2011
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