Groovy POGOs
We are all used to the plain old java object (POJO), but what do we know about the plain old groovy object (POGO). We all know that Groovy brings convenience to the Java langauage, but what does it it bring us when dealing with standard objects?
Consider the following POJO
1public class Customer {
2
3int id;
4String firstName;
5String lastName;
6String address;
7String city;
8String state;
9String postalCode;
10
11public Customer(int id, String firstName, String lastName, String address, String city, String state, String postalCode) {
12 this.id = id;
13 this.firstName = firstName;
14 this.lastName = lastName;
15 this.address = address;
16 this.city = city;
17 this.state = state;
18 this.postalCode = postalCode;
19}
20
21public int getId() {
22 return id;
23}
24
25public void setId(int id) {
26 this.id = id;
27}
28
29public String getFirstName() {
30 return firstName;
31}
32
33public void setFirstName(String firstName) {
34 this.firstName = firstName;
35}
36
37public String getLastName() {
38 return lastName;
39}
40
41public void setLastName(String lastName) {
42 this.lastName = lastName;
43}
44
45public String getAddress() {
46 return address;
47}
48
49public void setAddress(String address) {
50 this.address = address;
51}
52
53public String getCity() {
54 return city;
55}
56
57public void setCity(String city) {
58 this.city = city;
59}
60
61public String getState() {
62 return state;
63}
64
65public void setState(String state) {
66 this.state = state;
67}
68
69public String getPostalCode() {
70 return postalCode;
71}
72
73public void setPostalCode(String postalCode) {
74 this.postalCode = postalCode;
75}
76
77@Override
78public boolean equals(Object o) {
79 if (this == o) return true;
80 if (o == null || getClass() != o.getClass()) return false;
81
82 Customer customer = (Customer) o;
83
84 if (id != customer.id) return false;
85 if (address != null ? !address.equals(customer.address) : customer.address != null) return false;
86 if (city != null ? !city.equals(customer.city) : customer.city != null) return false;
87 if (firstName != null ? !firstName.equals(customer.firstName) : customer.firstName != null) return false;
88 if (lastName != null ? !lastName.equals(customer.lastName) : customer.lastName != null) return false;
89 if (postalCode != null ? !postalCode.equals(customer.postalCode) : customer.postalCode != null) return false;
90 if (state != null ? !state.equals(customer.state) : customer.state != null) return false;
91
92 return true;
93}
94
95@Override
96public int hashCode() {
97 int result = id;
98 result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
99 result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
100 result = 31 * result + (address != null ? address.hashCode() : 0);
101 result = 31 * result + (city != null ? city.hashCode() : 0);
102 result = 31 * result + (state != null ? state.hashCode() : 0);
103 result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
104 return result;
105}
106
107@Override
108public String toString() {
109 return "Customer{" +
110 "id=" + id +
111 ", firstName='" + firstName + ''' +
112 ", lastName='" + lastName + ''' +
113 ", address='" + address + ''' +
114 ", city='" + city + ''' +
115 ", state='" + state + ''' +
116 ", postalCode='" + postalCode + ''' +
117 '}';
118}
119}
Wow, thats a lot of boilerplate code for a simple object that contains a few pieces of data about a customer.
- fields which contains data about our Customer
- a constructor for creating a new Customer object
- getters and setters for accessing the fields
- equals and hashcode methods
- toString methods for each field
Every time a new field is added we must update the constructors, getters, setters, equals, hashcode, and toString methods
How would that look in groovy?
1package com.ellin.demo.com.ellin.demo.pogo
2
3import groovy.transform.EqualsAndHashCode
4import groovy.transform.ToString
5
6@EqualsAndHashCode(includes=['id','firstName','lastName','address','city','state','postalCode'])
7@ToString(includeNames=true)
8class Customer {
9
10 int id;
11 String firstName;
12 String lastName;
13 String address;
14 String city;
15 String state;
16 String postalCode;
17
18}
When compiled this POGO automatically is given getters and setters for its private fields and a map based constructor. The following code will compile just fine. The map based constructor is used to instantiate the object and the automatically included getter is used the retrieve the value of firstName.
1def customer = new Customer([id:1,firstName:'jeff',lastName:'ellin',address:'4 Yawkey Way',city:'Boston',state:'MA'])
2println customer.toString()
3assert customer.getFirstName() == 'jeff';
The output of the above code snippet is as follows
1com.ellin.demo.com.ellin.demo.pogo.Customer(id:1, firstName:jeff, lastName:ellin, address:4 Yawkey Way, city:Boston, state:MA, postalCode:null)
AST Class Annotations
I am also using two class annotations provided by Groovy, @EqualsAndHashCode and @ToString
@EqualsAndHashCode automatically adds the required logic for implementing .equals() and .hashCode() functions.
@ToString automatically generates a toString implementation based on the fields within the class.