PROPOSAL: Auto-assignment Parameters (original) (raw)

Reinier Zwitserloot reinier at zwitserloot.com
Wed Mar 25 13:27:36 PDT 2009


Context sensitive keyword creeps you out?

That's problematic. 'module' is already going to be one. I'd love an
alternative, but the few things I can come up with are all
considerably worse:

annotation, which is -slightly- more acceptable here than in other
places because generating methods and fields is what annotations are
supposed to do. However, they usually do so (via APT) in an entirely
new source file, not the same one.

(ab)using an existing keyword. 'const' and 'default' are the only
reasonable ones, and both of those are a stretch, in my book. I don't
like context sensitive keywords either, but as I've said before on
this list, that decision has been made: They will be part of java7,
and nothing on coin-dev is going to change that.

You should move away from the idea of 'this keyword does so much
magic!' and move towards the idea that this keyword simply declares
that the class is to have bean semantics. The amount of magic
performed by the compiler (right now, in java6) is enormous already.
The amount of magic performed by a C compiler is a few orders of
magnitude larger than that. As far as compiler magic goes, a simple
desugaring is in fact one of the simplest things it could possibly do.
As long as it doesn't cause surprises amongst the java programmer
crowd, that cannot reasonably be put forward as a con. Your suggestion
to have separate annotations is immediately going to run into the
following complaint: Oh, the boilerplate! Why can't we have 1
annotation that does it all!

Which brings us right back to my proposal.

Not auto-generating something that's already there is indeed a bit of
'magic', but is that really so far-fetched? What else should it do?
Generate an error? That's going to get the new vote as most annoying
javac error when the varargs thing finally (yay!) gets sorted via
Project Coin. There are very legitimate reasons to roll your own
methods for a few of the otherwise POJO-esque classes you'd write with
this feature. Backwards compatibility with other implementions of
hashCode() is one, and doing some processing on variables is another,
as is adding property support (with change listeners).

It would obviously be acceptable in a small data pojo to forego
hungarian notation. If it isn't - well, that won't be the first time
coding practices hurt more than they help. The thing with coding
practices is: Everyone has their own variant. One must at the very
least expect those practices to evolve a little in the face of
language changes, or one would be crippled.

What you are suggesting with parameterizing each aspect of the method
generation is going to become an entirely new programming language,
just so you can specify that you want to avoid the password field,
etcetera. The point of this proposal is very very specifically: For
the default case, we make it easy for you. If you dont like the
default case, that's perfectly allright. Roll your own method.

Just to drive the point home, here's a customized class versus how
you'd write it now. Note how the savings in size are almost ignorable,
and it now looks like a different programming language.

@GenerateConstructor(order={"bar", "foo"},
access=Access.PACKAGE_PRIVATE) @GenerateEquals(exclude={"baz"}) @GenerateHashCode(prime=97) @GenerateToString(JsonGenerator.class) public class ThisIsMyDataClass { @GenerateGetter @GenerateSetter(access=Access.PROTECTED) private int foo; @GenerateGetter private final String bar;

 //Oops! Forgot the annotation. Do I get any warning?
 private int baz;

}

vs:

public data class ThisIsMyDataClass { private int foo; private final String bar; private final int baz; }

The point of such a proposal is NOT to ease the making of getters and
setters. It's only to do what it says on the tin: Make data classes.

--Reinier Zwitserloot

On Mar 25, 2009, at 19:25, Lawrence Kesteloot wrote:

Reinier,

The idea of adding a context-sensitive keyword creeps me out. Also this is doing a lot of magic based on a single keyword. Can you tell, at a glance, what the keyword is doing? How much is generated? What are the (complex) rules for auto-generating a constructor? I would prefer a more explicit approach, driven by annotations. If we feel like boilerplate is annoying to write and bug-prone, then we could have a java.lang.gen package with various annotations for automatically generating code. Something like: @GenerateFullConstructor @GenerateEqualsAndHashCode @GenerateToString(JsonToStringGenerator.class) public class Foo { @GenerateGetter private final int x; @GenerateGetter private final int y; @GenerateGetter private final String foo; } Then it's explicit, each annotation can have parameters to modify its behavior, and people can pick and choose what they need. There's no magic about skipping the generation of toString if you've implemented it. Here's an example of useful parameters for such annotations: many people (myself included) like to prefix fields with something. I use "m" (mFoo), but others use an underscore, and some both (mfoo). You'd want the GenerateGetter annotation to be flexible about that. My code would look like this: @GenerateGetter(prefix = "m") private final String mFoo; Your "data" keyword would preclude this, forcing me to either drop my (company-mandated) coding standard for data classes, or have getters like "getMFoo()". With GenerateEqualsAndHashCode you might want to skip some fields. You might want to skip fields for GenerateToString also (e.g., password fields, or super long strings). Lawrence

On Wed, Mar 25, 2009 at 9:59 AM, Reinier Zwitserloot <reinier at zwitserloot.com> wrote: There's no reference for auto-POJOs anywhere, but this should allow you to write up all you need:

1. 'data' is a context sensitive keyword that is legal on classes. The 'data' keyword will add a number of members to the class. 2. For each field, a getter is generated, according to the beanspec (getFoo() or isFoo()). if the field is not final, a setter is generated, according to beanspec (setFoo()), and the field is considered a property of the data class, unless it is transient. If the field is public, the getter (and, if not final, the setter), is not generated. For all generated getters/setters, if the getter/ setter is already in the class, or already defined (that is; not abstract) in any supertype, then the getter/setter is not generated. protected and visible package protected members from supertypes are also included both for generating setters/getters and for hashCode/equals/toString/ clone (upcoming). 3. hashCode() and equals(): Existence of the hashCode() or equals() method in any supertype is not relevant (obviously; all objects have them!) 3a. If only one of hashCode() or equals() is present, a warning is generated and neither hashCode() or equals() is auto-generated. The warning basically explains that overriding one but not the other is a bug. 3b. If both hashCode() and equals() are present, nothing happens. 3c. If neither is present, a hashCode() and equals() method are generated. These involve all properties (non-transient visible fields). equals() is defined by: 3c1: First check if the other object is of the exact same class as ourselves; if not, false. If null, false. 3c2: For each primitive property, check via == if they are equal. If any isn't, false. 3c3: For each object property, call its .equals(). If they aren't, false. 3c4: return true. for hashCode(), do whatever eclipse does out of the box to auto- generate hashCode() methods. It basically looks like: final int PRIME = 31; int result = 1; for ( each field ) result = result*31 + field.hashCode(); return result; Where hashCode() is generated in obvious fashion for all primitives (xor the upper and lower bits of longs together, convert floats and doubles to long/intbits and use that, chars/bytes/ints/shorts's hashCode is their own value, booleans are translated to 11/17. 'null' becomes 3. 4. IF all fields are private, and IF all fields are final, AND the class does not contain any explicit constructors, AND the class has a no-args super constructor, then a constructor is generated that includes all fields, in order of definition, which simply assigns them to the fields. visible properties from supertypes aren't included. 5. A toString() method is generated, if not already present in the type, which produces a string like so: "MyClassName [field1=value, field2=value]", for all properties. (so, non-visible fields from supertypes and transient fields are skipped). 6 (optional). Each generated member gets a @Generated annotation, which is a new annotation ( java.lang.annotations.Generated ). These are @Documented and @Retention.RUNTIME.

Is it viable for coin? I'll write it up if so. --Reinier Zwitserloot

On Mar 25, 2009, at 14:41, james lowden wrote: I'd like to see both. Auto-getters/setters/equals/etc. would be really, really nice, but it would also be valuable to have a way of specifying a variety of different constructors to generate, which Mark's proposal would allow for. Example: public data class Foo { private final int x; private final int y; private final String foo; public Foo (this.x) {} public Foo (this.foo, this.y) {} }

(I eliminated the types from the automagical constructors, as they can be inferred by the compiler.) This would generate all three getters and setters, equals (), hashcode (), a general constructor taking in all 3 fields, and two additional constructors, one taking in just int x, and one taking in both String foo and int y. Reinier--is there a copy of your original proposal for auto-POJOs somewhere? -JL-

From: Reinier Zwitserloot <reinier at zwitserloot.com> Subject: Re: PROPOSAL: Auto-assignment Parameters To: "Mark Mahieu" <markmahieu at googlemail.com> Cc: coin-dev at openjdk.java.net Date: Tuesday, March 24, 2009, 8:34 PM I like where this is going, but I'd rather see a bigger setup for auto- POJO classes. I proposed this before and in most java circles (outside of coin-dev!), this idea was considered very useful: public data class Foo { private final int x; private final int y; private final String foo; }

The above class would auto-generate the getters, a constructor, hashCode, equals, and toString. You can add methods to it if you want, and if you define a method that would usually be auto-generated, it isn't auto-generated. So, if you need to do some extra processing before returning 'foo' via the getter, go right ahead. You can even add this later without breaking your API's compatibility. --Reinier Zwitserloot

On Mar 25, 2009, at 02:07, Mark Mahieu wrote: HTML version + prototype available at http://slm888.com/javac

Auto-assignment Parameters v0.1 AUTHOR(S): Mark Mahieu OVERVIEW FEATURE SUMMARY: Should be suitable as a summary in a language tutorial. An additional form of constructor parameter which provides automatic assignment of the parameter's value to an instance field. These parameters are implicitly declared as final to prevent unintentional assignments to the wrong variable in the constructor body. MAJOR ADVANTAGE: What makes the proposal a favorable change? Reduces the likelihood of some common coding errors relating to assignment of fields in a constructor, including those where: * a field is assigned to itself * the parameter is assigned instead of the field * the assignment to the field is missing entirely These and other errors are often caused by typos or code refactoring. MAJOR BENEFIT: Why is the platform better if the proposal is adopted? A programmer's intent is more clearly expressed for the extremely common case of assigning a constructor parameter directly to an instance field with the same name. MAJOR DISADVANTAGE: There is always a cost. As with any sugar which enables a more concise way of expressing an existing idiom, programmers may be tempted to use it even when the original form would be more appropriate. ALTERNATIVES: Can the benefits and advantages be had some way without a language change? IDEs and tools such as FindBugs are good at warning about the kind of errors mentioned above, however since the code in question is usually still valid to the compiler, they are limited in what action they can take by default. A language change can combine the ability to avoid these errors entirely with a more expressive idiom, resulting in an increased signal to noise ratio for readers of that code. EXAMPLES SIMPLE EXAMPLE: Show the simplest possible program utilizing the new feature. class C { int i; C(int this.i) {} } ADVANCED EXAMPLE: Show advanced usage(s) of the feature. class Proposal { private final String name; private final String author; private boolean suitableForCoin; private Integer score; public Proposal(String this.name, String this.author, boolean this.suitableForCoin, int this.score) { if (name.equals(“Auto-assignment Parameters”)) { suitableForCoin = true; // final so compile-time error } } // rest of class ... } DETAILS SPECIFICATION: Describe how the proposal affects the grammar, type system, and meaning of expressions and statements in the Java Programming Language as well as any other known impacts. The syntactic grammar is modified to include auto-assignment parameters for constructors: ConstructorDeclaratorRest: ConstructorParameters [throws QualifiedIdentifierList] MethodBody ConstructorParameters: ( [ConstructorParameterDecls] ) ConstructorParameterDecls: [final] [Annotations] Type ConstructorParameterDeclsRest ConstructorParameterDeclsRest: ConstructorParameterId [ , ConstructorParameterDecls] ... ConstructorParameterId ConstructorParameterId: VariableDeclaratorId this . VariableDeclaratorId super . VariableDeclaratorId An auto-assignment parameter is a formal parameter (JLSv3 §8.4.1) which specifies an instance field instead of an identifier. Its value is automatically assigned to the specified field. It may only be used in a constructor. The automatic assignment takes place after any explicit or implicit invocation of another constructor, and before any statements in the body of the constructor. A constructor which declares n auto-assignment parameters will perform n such automatic assignments, in the order that the parameters are declared. The parameter has the same name (JLSv3 §6.2) as the field to which it is assigned; replacing each auto-assignment parameter with a normal formal parameter with the same name would yield a constructor with an identical signature. As with a normal parameter, the parameter's name is entered into the scope of the constructor body (JLSv3 §6.3), and therefore shadows the field (JLSv3 §6.3.1) within that scope. It is a compile-time error if the field is not accessible from the constructor in which the parameter appears. It is a compile-time error if the declared type of the parameter is not assignment compatible (JLSv3 §5.2) with the field to which it is automatically assigned. If an unboxing conversion is required by an automatic assignment, any NullPointerException thrown as a result (JLSv3 §5.1.8) will contain the name of the field on which the conversion failed, which may be retrieved by calling getMessage() on the exception. Auto-assignment parameters are implicitly final, and follow the standard rules for final variables (JLSv3 §4.12.4). Explicitly declaring an auto-assignment parameter as final has no effect, and does not cause a compilation error. Auto-assignment parameters follow the standard definite assignment rules for formal parameters (JLSv3 §16.3). An auto-assignment parameter may be annotated. If an auto-assignment parameter is the last parameter in the list, and refers to a field of array type, it may be a variable arity parameter. COMPILATION: How would the feature be compiled to class files? Show how the simple and advanced examples would be compiled. Compilation can be expressed as at least one of a desugaring to existing source constructs and a translation down to bytecode. If a new bytecode is used or the semantics of an existing bytecode are changed, describe those changes, including how they impact verification. Also discuss any new class file attributes that are introduced. Note that there are many downstream tools that consume class files and that they may to be updated to support the proposal! Desugaring of the following class: class Foo { int value; Foo(Integer this.value) { System.out.println(value); } } would result in (unboxing desugaring omitted): class Foo { int value; Foo(final Integer value) { super(); if (value == null) throw new NullPointerException("value"); this.value = value; System.out.println(value); } } No changes to the classfile format are required. Tools which consume class files see the constructor signature as though it had been written using normal formal parameters. TESTING: How can the feature be tested? An initial set of jtreg tests is included in the prototype. LIBRARY SUPPORT: Are any supporting libraries needed for the feature? No REFLECTIVE APIS: Do any of the various and sundry reflection APIs need to be updated? This list of reflective APIs includes but is not limited to core reflection (java.lang.Class and java.lang.reflect.*), javax.lang.model.*, the doclet API, and JPDA. com.sun.source.tree.VariableTree would require an additional method which returns the auto-assigned field. OTHER CHANGES: Do any other parts of the platform need be updated too? Possibilities include but are not limited to JNI, serialization, and output of the javadoc tool. No MIGRATION: Sketch how a code base could be converted, manually or automatically, to use the new feature. Assignment statements in constructors for which all of the following are true can be considered suitable for conversion: * the lhs of the assignment is a non-static field * the rhs is a parameter with the same name as the field * there are no other assignments to the parameter anywhere in the constructor * there are no assignments to, or other uses of the field before the assignment statement in question (including invoked constructors) Such statements can be converted by: 1) replacing the parameter with an auto-assignment parameter (prefixing the existing parameter's identifier with 'this.'), and 2) removing the assignment statement COMPATIBILITY BREAKING CHANGES: Are any previously valid programs now invalid? If so, list one. No EXISTING PROGRAMS: How do source and class files of earlier platform versions interact with the feature? Can any new overloadings occur? Can any new overriding occur? The semantics of existing class files and legal source files are unchanged by this feature. REFERENCES EXISTING BUGS: Please include a list of any existing Sun bug ids related to this proposal. http://bugs.sun.com/bugdatabase/viewbug.do?bugid=6582394 (uses a 'setter' method rather than a constructor in its example) FindBugs bug patterns - http://findbugs.sourceforge.net/bugDescriptions.html * Self assignment of field (SAFIELDSELFASSIGNMENT) * Self comparison of field with itself (SAFIELDSELFCOMPARISON) * Nonsensical self computation involving a field (e.g., x & x) (SAFIELDSELFCOMPUTATION) * Self assignment of local variable (SALOCALSELFASSIGNMENT) * Nonsensical self computation involving a variable (e.g., x & x) (SALOCALSELFCOMPUTATION) * Uninitialized read of field in constructor (URUNINITREAD) * Unwritten field (UWFUNWRITTENFIELD) * Dead store to local variable (DLSDEADLOCALSTORE) * Dead store of null to local variable (DLSDEADLOCALSTOREOFNULL) URL FOR PROTOTYPE (optional): http://slm888.com/javac DESIGN ALTERNATIVES: The following variations have been explored and are worth mentioning: * Allow auto-assignment parameters on all non-abstract methods. This may be especially useful on the extremely common 'setter' methods in 'value object' classes. * Remove the requirement for (or even disallow) the type to be specified on auto-assignment parameters. Experimentation with this idea suggests that it may work quite well for constructors, further emphasising the difference in intent and semantics from those of normal parameters. However, it may not work so well in combination with auto-assignment parameters on all non-abstract methods, and requires a change to the order in which javac compiles classes (can still pass jtreg tests). * Allow an alternative name to be specified. This adds additional complexity to support a fractional percentage of cases, which could continue to use the existing coding pattern.



More information about the coin-dev mailing list