Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Code generating beans in Java

1,748 views

Published on

Taking an overview of the code generation of mutable and immutable beans in Java. Includes AutoValue, Immutables, Lombok and Joda-Beans.

Published in: Software

Code generating beans in Java

  1. 1. Bean generation Stop writing getters and setters Stephen Colebourne, @jodastephen Engineering Lead, OpenGamma September 2016 http://blog.joda.org
  2. 2. Stephen Colebourne ● Java Champion, regular conference speaker ● Best known for date & time - Joda-Time and JSR-310 ● More Joda projects - http://www.joda.org ● Major contributions in Apache Commons ● Blog - http://blog.joda.org ● Worked at OpenGamma for 6 years
  3. 3. Strata, from OpenGamma ● Open Source market risk library ● Valuation and risk calcs for finance ○ interest rate swap, FRA, CDS ● Great example of Java SE 8 coding style http://strata.opengamma.io/
  4. 4. Introduction ⇒
  5. 5. Why use beans? ● Beans are used in most applications ● Common denominator between applications & libraries ● ORMs (Hibernate, JPA, etc.) ● Serialization (Binary, JSON, XML, etc.) ● Mappers/configuration (Spring, Dozer, etc.)
  6. 6. What is a bean? ● JavaBean specification v1.01, from 1997 ● Focus on software components, COM/DCOM ● Manipulated visually in GUIs ● Java components within MS Word/Excel !!! ● References to floppy disks !!!
  7. 7. What is a bean? ● JavaBeans must extend java.awt.Component ● Created via standard factories ● No use of casts of instanceof checks ● Checked exceptions, not unchecked ● Communication via events ● Very specific rules around capitalization ● Use of BeanInfo and PropertyEditor
  8. 8. What is a bean? "Bean" != JavaBean
  9. 9. What is a mutable bean? ● Each tool has subtly different definition ● Most agree on ○ no-args constructor ○ getters match getXxx() ○ setters match setXxx() ○ equals() / hashCode() / toString()
  10. 10. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for mutable bean /** Represents a person. */ public class Person { /** The forename of the person. */ private String forename; /** The surname of the person. */ private String surname; /** The birth date of the person. */ private LocalDate birthDate;
  11. 11. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for mutable bean /** Creates an empty instance of Person. */ public Person() {} /** Gets the forename of the person. */ public String getForename() { … } /** Sets the forename of the person. */ public void setForename(String forename) { … } // same for surname/birthDate
  12. 12. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for mutable bean /** Compares this person to another. */ public boolean equals(Object obj) { … } /** Returns a suitable hash code. */ public int hashCode() { … } /** Returns a string summary of this object. */ public String toString() { … }
  13. 13. What is an immutable bean? ● "Beans" has traditionally implied mutability ● Many tools can't handle immutable beans ● No setters, may have "withers" ● Class must be final, with final fields ● Factory or builder instead of constructor
  14. 14. Why immutable? ● Thread-safe ● Java Memory Model guarantees ● No need to trust other methods not to modify ● State checked and valid on construction ● Nulls can be eliminated
  15. 15. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for immutable bean /** Represents a person. */ public final class Person { /** The forename of the person. */ private final String forename; /** The surname of the person. */ private final String surname; /** The birth date of the person. */ private final LocalDate birthDate;
  16. 16. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for immutable bean /** Obtains an instance of Person. */ public Person of( String surname, String forename, LocalDate date) { … } /** Gets the forename of the person. */ public String getForename() { … } /** Returns a copy with the specified forename. */ public Person withForename(String forename) { … } // same for surname/birthDate
  17. 17. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Pattern for immutable bean /** Compares this person to another. */ public boolean equals(Object obj) { … } /** Returns a suitable hash code. */ public int hashCode() { … } /** Returns a string summary of this object. */ public String toString() { … }
  18. 18. Pattern for immutable bean ● May prefer to have a builder instead of a factory // factory Person person = Person.of("Stephen", "Colebourne", date); // or builder Person person = Person.builder() .forename("Stephen") .surname("Colebourne") .birthDate(date) .build();
  19. 19. What is a VALJO? ● POJO - Plain Old Java Object ● VALJO - Value Java Object ○ http://blog.joda.org/2014/03/valjos-value-java-objects.html ● No use of identity - equal is interchangeable ● Immutable bean with extra restrictions ○ may be suitable for conversion to value types in Java 10/11 ○ logical state clearly defined and used in equals()/hashCode() ○ comparable consistent with equals() ○ constructor must be private ○ formal string representation that can be parsed
  20. 20. Creating beans
  21. 21. How to create beans ● Lots of different ways to create beans ● Best option depends on use case ● Mutable vs Immutable
  22. 22. Option 1 - Manual ● Write each bean manually ● Deathly boring ● Double deathly boring for immutable beans ● Error-prone ● Probably not tested or code reviewed
  23. 23. Option 2 - Another JVM language ● Kotlin code much shorter // Kotlin immutable bean data class Person( val forename: String, val surname: String, val birthDate: LocalDate)
  24. 24. Option 2 - Another JVM language ● Groovy code much shorter // Groovy mutable bean class Customer { String forename String surname LocalDate birthDate
  25. 25. Option 3 - Language change ● New language feature in Java ● Perhaps like Kotlin/Groovy ● Doesn't help us now ● Likely to be restrictive
  26. 26. Option 4 - IDE generation ● Eclipse / IntelliJ / NetBeans can generate the code ○ Eclipse uses Ctrl+Alt+S followed by R / O / H / S ○ IntelliJ uses Alt+Insert ● One time generation, doesn't handle change ● Can you express field is not null? ● Can you generate immutable builders? ● Still not tested or code reviewed
  27. 27. Option 5 - Use a tool ● AutoValue ● Immutables ● Lombok ● Joda-Beans ● (VALJOGen) ● (POJOmatic)
  28. 28. AutoValue
  29. 29. AutoValue ● Annotation processor ● Open Source, from Google ○ https://github.com/google/auto/tree/master/value
  30. 30. Annotation processing ● Additional step during compilation ● Compiler calls annotation processor ● Processor generates additional files ● If generated file is a .java file then it is compiled
  31. 31. Annotation processing Person (abstract) PersonImpl (concrete) Compilation finds annotations and code generates You write abstract class
  32. 32. Annotation processing ● Maven ○ just add the dependency ● Eclipse with Maven ○ install m2e-apt plugin ○ turn it on in the preferences ● IntelliJ with Maven ○ turn it on in the preferences
  33. 33. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); AutoValue: your code @AutoValue public abstract class Person { public static Person of(String name, LocalDate date) { return new AutoValue_Person(name, date); } public abstract String getName(); public abstract LocalDate getBirthDate(); }
  34. 34. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); AutoValue: generated @Generated("com.google.auto.value.processor.AutoValueProcessor") final class AutoValue_Person extends Person { private final String name; private final LocalDate birthDate; // constructor, getters, equals, hashCode, toString
  35. 35. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); AutoValue builder: your code @AutoValue public abstract class Person { public static Builder builder() { return new AutoValue_Person.Builder(); } @AutoValue.Builder public static abstract class Builder { public abstract Builder name(String name); public abstract Builder birthDate(LocalDate date); public abstract Person build(); } public abstract Builder toBuilder();
  36. 36. AutoValue options ● Handles name() or getName() convention ● Can override (underride) equals/hashCode/toString ● Can add any other method ● Can change fields to nullable using @Nullable ● Supports memoized fields ● Builder can have sub-builders for collections ● Pattern to handle builder validation ● Extensions API, but undocumented
  37. 37. AutoValue pros/cons ● Callers use Person like a normal bean ○ but they can see class is abstract ● Generated code is package scoped ○ you must write outline builder and/or static factory method ○ quite a lot of code still to write ● Simple, does sensible thing for most use cases ○ not that many options ● Only for immutable beans
  38. 38. Immutables
  39. 39. Immutables ● Annotation processor ● Open Source ○ https://immutables.github.io/ ● Reacts to use Guava if available
  40. 40. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Immutables: your code @Value.Immutable public interface Person { String getName(); LocalDate getBirthDate(); }
  41. 41. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Immutables: generated @SuppressWarnings("all") @Generated({"Immutables.generator", "ImmutablePerson"}) public final class ImmutablePerson implements Person { private final String name; private final LocalDate birthDate; private ImmutablePerson(String name, LocalDate birthDate) { this.name = name; this.birthDate = birthDate; } // getters, withers, equals, hashCode, toString, builder
  42. 42. Immutables options ● Can generate from abstract class or interface ● Handles name() or getName() convention ● Can override (underride) equals/hashCode/toString ● Can add any other method ● Pre-computed hash code ● Instance interning ● and lots more!
  43. 43. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Immutables: hide implementation @Value.Immutable @Value.Style(visibility = PACKAGE) public abstract class Person extends WithPerson { public String getName(); public LocalDate getBirthDate(); public static class Builder implements ImmutablePerson.Builder {} }
  44. 44. Immutables pros/cons ● Many options and ways to generate ○ takes time to choose best option ● Callers see and use generated class (by default) ○ hiding generated class possible if you write more code ● Mutable beans supported ○ more like builders, do not follow JavaBeans spec
  45. 45. Lombok
  46. 46. Lombok ● Internal APIs ● Open Source ○ https://projectlombok.org/ ● Uses agents and annotation processors ● Works best with Eclipse, but requires installing
  47. 47. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Lombok: your code - mutable @Data public class Person { private String name; private LocalDate birthDate; }
  48. 48. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Lombok: generated- mutable @Data public class Person { private String name; private LocalDate birthDate; // constructor, getters, setters, equals, hashCode, toString }
  49. 49. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Lombok: your code - immutable @Value public class Person { private String name; private LocalDate birthDate; }
  50. 50. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Lombok: generated - immutable @Value public final class Person { private final String name; private final LocalDate birthDate; // constructor, getters, equals, hashCode, toString, builder }
  51. 51. Lombok options ● Over 15 annotations and tweaks ● Use other annotations for more control ● Can add any other method
  52. 52. Lombok pros/cons ● No second class, proper immutable bean ○ resulting bean is exactly the same as manually written ● Works best with Maven and Eclipse ○ installation in Eclipse not via a standard plugin ● Uses internal API hackery ○ higher risk option ● Generated code is invisible ○ cannot step in when debugging ○ can "delombok" to code generated code
  53. 53. Joda-Beans
  54. 54. Joda-Beans ● Same-file regenerator ● Open Source, by me @jodastephen ○ http://www.joda.org/joda-beans/ ● Adds autogenerated block to your code ● Generation using Maven/Gradle, on-save in Eclipse
  55. 55. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Joda-Beans: your code - mutable @BeanDefinition public class Person implements Bean { @PropertyDefinition private String name; @PropertyDefinition private LocalDate birthDate; }
  56. 56. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Joda-Beans: generated - mutable @BeanDefinition public class Person implements Bean { @PropertyDefinition private String name; @PropertyDefinition private LocalDate birthDate; // ---- AUTOGENERATED START ---- // getters, setters, equals, hashCode, toString, properties // ----- AUTOGENERATED END ----- }
  57. 57. Properties ● C# and most other languages have properties ● Higher level than a field ● Bean is a set of properties ● Can list, get and set properties ○ like reflection ● Very useful abstraction for frameworks
  58. 58. Joda-Beans properties ● Bean provides abstraction for properties ● MetaBean acts like Class ● Can loop over MetaProperty for each property ○ meta bean acts as a hash-map of property name to property ○ no reflection ● BeanBuilder allows a bean to be created ○ this allows immutable beans to be created one property at a time
  59. 59. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Joda-Beans: your code immutable @BeanDefinition public class Person implements ImmutableBean { @PropertyDefinition(validate = "notNull") private String name; @PropertyDefinition(validate = "notNull") private LocalDate birthDate; }
  60. 60. // Java 7 List<Person> people = loadPeople(); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { return p1.name.compareTo(p2.name); } }); Joda-Beans: generated immutable @BeanDefinition public class Person implements ImmutableBean { @PropertyDefinition(validate = "notNull") private String name; @PropertyDefinition(validate = "notNull") private LocalDate birthDate; // ---- AUTOGENERATED START ---- // getters, equals, hashCode, toString, properties, builder // ----- AUTOGENERATED END ----- }
  61. 61. Joda-Beans options ● Annotations allow field-level and bean-level control ● Control scope/style for getters/setters ● Supports pragmatic optional usage ● Can add any validation code ● Defaults/cross-validation for immutable bean builders ● Can default one property from another in builder ● Can fully override constructor
  62. 62. Joda-Beans pros/cons ● No second class, proper immutable bean ○ resulting bean is same as manually written ● Adds abstraction for properties without reflection ○ key reason to use Joda-Beans, but requires runtime dependency ● Built in XML, JSON, Binary serialization ● Generated code is visible for debugging ○ more code to checkin, but easy to ignore in review ○ proper Javadoc ● Plugin for Maven, Gradle and Eclipse with M2E ○ anyone want to volunteer to write an IntelliJ one?
  63. 63. Comparisons
  64. 64. Comparisons ● Annotation processor ○ AutoValue ○ Immutables ● Internal APIs ○ Lombok ● Same file regenerator ○ Joda-Beans
  65. 65. Comparisons ● All can generate a lot of useful code ○ getters, factories, builders, equals, hashCode, toString ● Some can generate mutable beans ● Only Joda-Beans generates C# style properties ● Each project has a different trade-off
  66. 66. Same class vs Generated class ● Same class (Joda-Beans, Lombok) ○ your code involves writing fields ○ caller sees concrete class ○ properly immutable ● Generated class (AutoValue, Immutables) ○ your code involves writing methods (more code) ○ caller sees abstract class or interface ○ is it really immutable? ○ IDE rename class typically breaks code
  67. 67. Collections ● Immutable beans should use Guava ImmutableList ● Builder needs to take List and convert internally ● All except Lombok do this correctly
  68. 68. Installation requirements ● AutoValue & Immutables use annotation processor ○ must be configured in IDE, extra plugin for Eclipse ● Lombok uses internal API hackery ○ requires specific Eclipse installation ● Joda-Beans runs as separate code regenerator ○ for Eclipse, just needs standard M2E
  69. 69. AutoValue vs Immutables ● AutoValue ○ you write abstract class ○ generated class is package-scoped ○ cannot generate static factory ○ must write builder outline manually ● Immutables ○ you write interface or abstract class ○ generated class is public (can be made private or package-scoped) ○ static factory and builder in generated class ○ lots of flexibility
  70. 70. Valid on checkout ● AutoValue, Immutables, Lombok ○ code invalid in IDE on checkout unless configured ○ only your code checked in ● Joda-Beans ○ code valid in IDE on checkout, even if not configured ○ your code and generated code checked in
  71. 71. Evaluate yourself ● GitHub project with everything setup for comparison ○ https://github.com/jodastephen/compare-beangen ● Includes 4 tools discussed ○ plus VALJOGen, POJOmatic, Eclipse wizards and IntelliJ wizards ● Good project to play with each tool
  72. 72. Summary ✯
  73. 73. Summary ● All four tools have their sweet spot and trade off ○ AutoValue - simplicity, abstract class ○ Immutables - comprehensive, abstract class or interface ○ Lombok - almost a language extension, hacky implementation ○ Joda-Beans - C# style properties, runtime dependency
  74. 74. Summary - personal view ● Use Joda-Beans if you like properties ○ Code should be valid on checkout ○ Immutable beans should be final ○ Want C# style properties ○ Hence I wrote and use Joda-Beans ● Otherwise, use Immutables @jodastephen http://blog.joda.org

×