PRE-PROPOSAL: Named method parameters with defaults.
Reinier Zwitserloot
reinier at zwitserloot.com
Sat Mar 21 15:21:35 PDT 2009
I think without having a way to specify default values, this really
isn't going to replace the builder pattern. Being allowed to re-order
should also be on the table, a the very least.
Here's all the technical detail of a proposal that might, maybe, fit
inside project coin's scope.
First off:
Of all methods that have the same name (but different paramlists, e.g.
overloading), only one is allowed to be named in the first place. If
you're overriding it and also want YOUR version to be named, then your
parameter list must be in the exact same order *AND* each parameter
must have the exact same name as the overridden method (that's a new
compiler check). If you're overridding a non-named method and you turn
it into a named parameter method, that's fine. If you're overriding a
named method and you don't make it named, that's fine too - but, it is
still considered named. (100% analogous to how you can de-varargs a
method in a subtype, but the compiler still considers it varargs).
That should solve overloading and inheritance problems.
Then, the syntax itself:
public named void foo(int x = 5, List<String> list = new ArrayList(),
String foo) {
//as normal
}
The 'named' is a new context sensitive keyword (if 'module' can be
one, then this isn't too much of a stretch), and, only for named
methods, you can supply expressions that serve as defaults. Each
expression is evaluated exactly once, during your class initialization
(so, the ArrayList above is SHARED amongst all method invocations! -
I'd force it to be immutable but java does not have such a facility).
The system works by translating all value expressions into public
final fields with a specific name. Javac, when finding a caller to a
named method, will re-order as needed and add references to those
fields to fill in the gaps. So, the above becomes:
public final int foo$x = 5;
public final List<String> foo$list = new ArrayList();
public void foo(int x, List<String> list, String foo) {
//as normal
}
The fields have the Synthetic flag set. The method is compiled with
param names, exactly the same way names are saved when turning on
debug information (regardless of the actual -g:vars flag value), and a
new flag called 'Named'.
The following call:
ClassName.foo(foo: "hello", x:10);
is compiled exactly the same way the following would be compiled:
ClassName.foo(10, ClassName.foo$list, "hello");
If you use the named syntax, then every parameter MUST be named. If
you use no names, that's fine, but you don't get any of the sugar -
You'd have to supply all 3 parameters, in the right order.
I'm fairly sure this is migration and source compatible, even in the
case of subclasses, is not ambiguous (even in light of overriding
classes, implementing types, and overloading the method), introduces
no high-impact new features, other than forcing -g:vars (which I think
is minor; most compilers already do this, especially for libraries, so
that auto-complete can offer nicer names for the parameters than
'arg0', 'arg1', and 'arg2'). Having the compiler dig through a type's
entire parent tree including interfaces to figure out if the method is
named in any of the subtypes seems high impact, but as I mentioned:
It's not - javac already does this to figure out if an array in the
last position is declared as a varargs in any supertype, and if so,
treats that as a vararg.
Specifically, any library out there can update their classes and even
their interfaces with 'named' keywords WITHOUT breaking any kind of
compatibility; existing class files will continue to work, and
recompiling old code in a javac v7 without accounting for the 'named'
feature does not break those either.
To highlight the way the 'named' property is inherited from any
supertype with an example:
public interface SuperType {
void foo(String... args);
named void bar(String arg);
}
public class Baz implements SuperType {
void foo(String[] args);
void bar(String arg);
}
Baz b = new Baz();
b.foo("a", "b"); //legal TODAY
b.bar(arg: "foo"); //would be legal too
Even if the subclass does not have -g:vars on, the names are still
available via the supertype. In fact, without the 'named' modifier,
the names of the parameters in a subclass are IGNORED (they could be
different, and that would hence not be backwards compatible, which is
bad). So, any 'named' marked methods must have equal names, but only
one of the methods needs to be marked as such, and it 'infects' all
subtypes. The syntax will not clash much with the parser, and the
context sensitive keyword is extremely unlikely to introduce problems
(if you have a *type* named 'named', you'd break things, but types are
usually capitalized. The 'module' keyword suffers from the same issue,
I believe.
Three questions:
A) Is this a good idea? Do you like it? Would you use it? (I think its
stellar, but then, I'm proposing it).
B) Did I miss something (is it more complicated than I make it out to
be), or is something unclear? Specifically, did I miss something that
does not make it backwards, migration, and source compatible?
C) Might this be within scope for project coin? I'll definitely write
it up if it stands a chance.
--Reinier Zwitserloot
On Mar 21, 2009, at 18:56, Marek Kozieł wrote:
> Builders are really great tool, but they need to be written manually.
>
> In other way there will be problem, if final object need contains data
> and builder only key for that data.
>
>
>
> --
> Pozdrowionka. / Regards.
> Lasu aka Marek Kozieł
>
> http://lasu2string.blogspot.com/
>
More information about the coin-dev
mailing list