[ 
https://issues.apache.org/jira/browse/AVRO-839?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13091626#comment-13091626
 ] 

James Baldassari commented on AVRO-839:
---------------------------------------

bq. If you like. Or we could file it as a followup issue.

I implemented this.  It wasn't too much work, and it's easier to just get it 
over with now than risk some potentially backwards-incompatible change after 
people start using the new builder stuff.  I used the strategy described in my 
earlier comment: "foo" -> "getFoo$0()", "Foo" -> "getFoo$1()" where the "0" is 
always for the lowercase field name and the "1" is always for the uppercase 
field name.  It's easy to change this if you think a different naming scheme 
would be better.

I merged your recent trunk commits, so this patch should apply cleanly to 
trunk.  After merging the updates from trunk I found that 
TestSpecificCompilerTool was failing because it was comparing the compiler 
output to a pre-builder generated class.  I fixed this problem by copying the 
generated class that includes the builder modifications to 
src/test/compiler/output/.

One other change I made was a minor fix to the generated mutator methods in 
record.vm.  If the field happened to be named "value" the mutator would not 
work (because it was doing something like "field = value" instead of 
"this.field = value".

bq. One other minor improvement that I'd like to see is the addition of a 
package.html file for org.apache.avro.data

Is this something you would like me to do?

> Implement builder pattern in generated record classes that sets default 
> values when omitted
> -------------------------------------------------------------------------------------------
>
>                 Key: AVRO-839
>                 URL: https://issues.apache.org/jira/browse/AVRO-839
>             Project: Avro
>          Issue Type: Improvement
>          Components: java
>            Reporter: James Baldassari
>            Assignee: James Baldassari
>             Fix For: 1.6.0
>
>         Attachments: AVRO-839-v2.patch, AVRO-839-v3.patch, AVRO-839-v4.patch, 
> AVRO-839-v4.patch, AVRO-839.patch, AVRO-839.patch, AVRO-839.patch
>
>
> This is an idea for an improvement to the SpecificCompiler-generated record 
> classes.  There are two main issues to address:
> # Default values specified in schemas are only used at read time, not when 
> writing/serializing records.  For example, a NullPointerException is thrown 
> when attempting to write a record that has an uninitialized array or string 
> type.  I'm sure this was done for good reasons, like giving users maximum 
> control and preventing unnecessary garbage collection, but I think it's also 
> somewhat confusing and unintuitive for new users (myself included).
> # Users have to create their own factory classes/methods for every record 
> type, both to ensure that all non-primitive members are initialized and to 
> facilitate the construction and initialization of record instances (i.e. 
> constructing and setting values in a single statement).
> These issues have been discussed previously here:
> * [http://search-hadoop.com/m/iDVTn1JVeSR1]
> * AVRO-726
> * AVRO-770
> * [http://search-hadoop.com/m/JuY1V16pwxh1]
> I'd like to propose a solution that is used by at least one other messaging 
> framework.  For each generated record class there will be a public static 
> inner class called Builder.  The Builder inner class has the same fields as 
> the record class, as well as accessors and mutators for each of these fields. 
>  Whenever a mutator method is called, the Builder sets a boolean flag 
> indicating that the field has been set.  All mutators return a reference to 
> 'this', so it's possible to chain a series of setter invocations, which makes 
> it really easy to construct records in a single statement.  The Builder also 
> has a build() method which constructs a record instance using the values that 
> were set in the Builder.  When the build() method is invoked, if there are 
> any fields that have not been set but have default values as defined in the 
> schema, the Builder will set the values of these fields using their defaults.
> One nice thing about implementing the builder pattern in a static inner 
> Builder class rather than in the record itself is that this enhancement will 
> be completely backwards-compatible with existing code.  The record class 
> itself would not change, and the public fields would still be there, so 
> existing code would still work.  Users would have the option to use the 
> Builder or continue constructing records manually.  Eventually the public 
> fields could be phased out, and the record would be made immutable.  All 
> changes would have to be done through the Builder.
> Here is an example of what this might look like:
> {code}
> // Person.newBuilder() returns a new Person.Builder instance
> // All Person.Builder setters return 'this' allowing us to chain set calls 
> together for convenience
> // Person.Builder.build() returns a Person instance after setting any 
> uninitialized values that have defaults
> Person me = 
> Person.newBuilder().setName("James").setCountry("US").setState("MA").build();
> // We still have direct access to Person's members, so the records are 
> backwards-compatible
> me.state = "CA";
> // Person has accessor methods now so that the public fields can be phased 
> out later
> System.out.println(me.getState());
> // No NPE here because the array<Person> field that stores this person's 
> friends has been automatically 
> // initialized by the Builder to a new java.util.ArrayList<Person> due to a 
> @java_class annotation in the IDL
> System.out.println(me.getFriends().size());
> {code}
> What do people think about this approach?  Any other ideas?

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to