1. Bean generation
Stop writing getters and setters
Stephen Colebourne, @jodastephen
Engineering Lead, OpenGamma
September 2016
http://blog.joda.org
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. 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/
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. 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. 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
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. // 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. // 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. // 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. 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. 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. // 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. // 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. // 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. 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. 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
21. How to create beans
● Lots of different ways to create beans
● Best option depends on use case
● Mutable vs Immutable
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. 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. Option 2 - Another JVM language
● Groovy code much shorter
// Groovy mutable bean
class Customer {
String forename
String surname
LocalDate birthDate
25. Option 3 - Language change
● New language feature in Java
● Perhaps like Kotlin/Groovy
● Doesn't help us now
● Likely to be restrictive
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. Option 5 - Use a tool
● AutoValue
● Immutables
● Lombok
● Joda-Beans
● (VALJOGen)
● (POJOmatic)
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
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. // 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. // 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. // 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. 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. 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
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. // 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. 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. // 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. 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
46. Lombok
● Internal APIs
● Open Source
○ https://projectlombok.org/
● Uses agents and annotation processors
● Works best with Eclipse, but requires installing
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. // 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. // 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. // 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. Lombok options
● Over 15 annotations and tweaks
● Use other annotations for more control
● Can add any other method
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
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. // 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. // 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. 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. 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. // 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. // 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. 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. 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?
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. 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. Collections
● Immutable beans should use Guava ImmutableList
● Builder needs to take List and convert internally
● All except Lombok do this correctly
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. 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. 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. 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
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. 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