0
# Builder Utilities
1
2
Apache Commons Lang provides powerful builder pattern utilities that simplify the creation of equals(), hashCode(), toString(), and compareTo() methods. These builders follow fluent interfaces and handle complex cases including null values, arrays, and nested objects automatically.
3
4
## Core Builder Classes
5
6
### ToStringBuilder - Fluent toString() Construction
7
8
Creates readable string representations of objects with extensive customization options:
9
10
```java { .api }
11
import org.apache.commons.lang3.builder.ToStringBuilder;
12
import org.apache.commons.lang3.builder.ToStringStyle;
13
```
14
15
#### Basic Usage
16
17
```java { .api }
18
public class Person {
19
private String name;
20
private int age;
21
private List<String> hobbies;
22
23
@Override
24
public String toString() {
25
return new ToStringBuilder(this)
26
.append("name", name)
27
.append("age", age)
28
.append("hobbies", hobbies)
29
.toString();
30
}
31
}
32
33
// Output: Person@1a2b3c4d[name=John Doe,age=25,hobbies=[reading, gaming]]
34
```
35
36
#### ToStringBuilder Methods
37
38
```java { .api }
39
// Basic field appending
40
public ToStringBuilder append(String fieldName, Object obj)
41
public ToStringBuilder append(String fieldName, boolean value)
42
public ToStringBuilder append(String fieldName, byte value)
43
public ToStringBuilder append(String fieldName, char value)
44
public ToStringBuilder append(String fieldName, double value)
45
public ToStringBuilder append(String fieldName, float value)
46
public ToStringBuilder append(String fieldName, int value)
47
public ToStringBuilder append(String fieldName, long value)
48
public ToStringBuilder append(String fieldName, short value)
49
50
// Array appending
51
public ToStringBuilder append(String fieldName, Object[] array)
52
public ToStringBuilder append(String fieldName, boolean[] array)
53
public ToStringBuilder append(String fieldName, byte[] array)
54
// ... similar for other primitive arrays
55
56
// Advanced appending
57
public ToStringBuilder appendAsObjectToString(Object object)
58
public ToStringBuilder appendSuper(String superToString)
59
public ToStringBuilder appendToString(String toString)
60
```
61
62
#### ToStringStyle Options
63
64
```java { .api }
65
// Predefined styles
66
ToStringStyle.DEFAULT_STYLE // ClassName@hashcode[field=value,field=value]
67
ToStringStyle.MULTI_LINE_STYLE // Each field on separate line
68
ToStringStyle.NO_FIELD_NAMES_STYLE // Values without field names
69
ToStringStyle.SHORT_PREFIX_STYLE // ClassName[field=value,field=value]
70
ToStringStyle.SIMPLE_STYLE // field=value,field=value
71
ToStringStyle.NO_CLASS_NAME_STYLE // [field=value,field=value]
72
ToStringStyle.JSON_STYLE // {"field":"value","field":"value"}
73
```
74
75
**Usage Examples:**
76
```java { .api }
77
public class Employee {
78
private String name = "John Doe";
79
private int id = 12345;
80
private String[] skills = {"Java", "Python", "SQL"};
81
private Address address = new Address("123 Main St", "New York");
82
83
// Default style
84
public String toString() {
85
return new ToStringBuilder(this)
86
.append("name", name)
87
.append("id", id)
88
.append("skills", skills)
89
.append("address", address)
90
.toString();
91
}
92
// Output: Employee@1a2b3c4d[name=John Doe,id=12345,skills={Java,Python,SQL},address=Address@5f6g7h8i[...]]
93
94
// JSON style
95
public String toJsonString() {
96
return new ToStringBuilder(this, ToStringStyle.JSON_STYLE)
97
.append("name", name)
98
.append("id", id)
99
.append("skills", skills)
100
.toString();
101
}
102
// Output: {"name":"John Doe","id":12345,"skills":["Java","Python","SQL"]}
103
104
// Multi-line style
105
public String toMultiLineString() {
106
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
107
.append("name", name)
108
.append("id", id)
109
.append("skills", skills)
110
.toString();
111
}
112
// Output:
113
// Employee@1a2b3c4d[
114
// name=John Doe
115
// id=12345
116
// skills={Java,Python,SQL}
117
// ]
118
119
// No field names style
120
public String toSimpleString() {
121
return new ToStringBuilder(this, ToStringStyle.NO_FIELD_NAMES_STYLE)
122
.append(name)
123
.append(id)
124
.append(skills)
125
.toString();
126
}
127
// Output: Employee@1a2b3c4d[John Doe,12345,{Java,Python,SQL}]
128
}
129
```
130
131
#### ReflectionToStringBuilder - Automatic toString()
132
133
Automatically generates toString() using reflection:
134
135
```java { .api }
136
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
137
138
public class Product {
139
private String name = "Laptop";
140
private double price = 999.99;
141
private boolean inStock = true;
142
143
@Override
144
public String toString() {
145
return ReflectionToStringBuilder.toString(this);
146
}
147
// Automatically includes all fields
148
149
// With custom style
150
public String toJsonString() {
151
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
152
}
153
154
// Exclude specific fields
155
public String toStringExcludePrice() {
156
return ReflectionToStringBuilder.toStringExclude(this, "price");
157
}
158
159
// Include only specific fields
160
public String toStringOnlyName() {
161
return ReflectionToStringBuilder.toStringInclude(this, "name");
162
}
163
}
164
```
165
166
### EqualsBuilder - Fluent equals() Construction
167
168
Creates robust equals() methods with null safety and type checking:
169
170
```java { .api }
171
import org.apache.commons.lang3.builder.EqualsBuilder;
172
```
173
174
#### Basic EqualsBuilder Usage
175
176
```java { .api }
177
public class Person {
178
private String firstName;
179
private String lastName;
180
private int age;
181
private Date birthDate;
182
private String[] hobbies;
183
184
@Override
185
public boolean equals(Object obj) {
186
if (this == obj) return true;
187
if (obj == null || getClass() != obj.getClass()) return false;
188
189
Person person = (Person) obj;
190
191
return new EqualsBuilder()
192
.append(firstName, person.firstName)
193
.append(lastName, person.lastName)
194
.append(age, person.age)
195
.append(birthDate, person.birthDate)
196
.append(hobbies, person.hobbies)
197
.isEquals();
198
}
199
}
200
```
201
202
#### EqualsBuilder Methods
203
204
```java { .api }
205
// Basic field comparison
206
public EqualsBuilder append(Object lhs, Object rhs)
207
public EqualsBuilder append(boolean lhs, boolean rhs)
208
public EqualsBuilder append(byte lhs, byte rhs)
209
public EqualsBuilder append(char lhs, char rhs)
210
public EqualsBuilder append(double lhs, double rhs)
211
public EqualsBuilder append(float lhs, float rhs)
212
public EqualsBuilder append(int lhs, int rhs)
213
public EqualsBuilder append(long lhs, long rhs)
214
public EqualsBuilder append(short lhs, short rhs)
215
216
// Array comparison
217
public EqualsBuilder append(Object[] lhs, Object[] rhs)
218
public EqualsBuilder append(boolean[] lhs, boolean[] rhs)
219
// ... similar for other primitive arrays
220
221
// Advanced comparison
222
public EqualsBuilder appendSuper(boolean superEquals)
223
public boolean isEquals()
224
```
225
226
#### Reflection-Based equals()
227
228
```java { .api }
229
public class Employee {
230
private String id;
231
private String name;
232
private String department;
233
private double salary;
234
235
@Override
236
public boolean equals(Object obj) {
237
return EqualsBuilder.reflectionEquals(this, obj);
238
}
239
240
// Exclude specific fields from comparison
241
public boolean equalsIgnoreSalary(Object obj) {
242
return EqualsBuilder.reflectionEquals(this, obj, "salary");
243
}
244
245
// Include only up to a certain class in hierarchy
246
public boolean equalsShallow(Object obj) {
247
return EqualsBuilder.reflectionEquals(this, obj, false, Employee.class);
248
}
249
}
250
```
251
252
### HashCodeBuilder - Fluent hashCode() Construction
253
254
Creates consistent hashCode() methods that work correctly with equals():
255
256
```java { .api }
257
import org.apache.commons.lang3.builder.HashCodeBuilder;
258
```
259
260
#### Basic HashCodeBuilder Usage
261
262
```java { .api }
263
public class Person {
264
private String firstName;
265
private String lastName;
266
private int age;
267
private Date birthDate;
268
269
@Override
270
public int hashCode() {
271
return new HashCodeBuilder(17, 37) // Two different odd prime numbers
272
.append(firstName)
273
.append(lastName)
274
.append(age)
275
.append(birthDate)
276
.toHashCode();
277
}
278
}
279
```
280
281
#### HashCodeBuilder Methods
282
283
```java { .api }
284
// Basic field hashing
285
public HashCodeBuilder append(Object object)
286
public HashCodeBuilder append(boolean value)
287
public HashCodeBuilder append(byte value)
288
public HashCodeBuilder append(char value)
289
public HashCodeBuilder append(double value)
290
public HashCodeBuilder append(float value)
291
public HashCodeBuilder append(int value)
292
public HashCodeBuilder append(long value)
293
public HashCodeBuilder append(short value)
294
295
// Array hashing
296
public HashCodeBuilder append(Object[] array)
297
public HashCodeBuilder append(boolean[] array)
298
// ... similar for other primitive arrays
299
300
// Advanced hashing
301
public HashCodeBuilder appendSuper(int superHashCode)
302
public int toHashCode()
303
```
304
305
#### Reflection-Based hashCode()
306
307
```java { .api }
308
public class Product {
309
private String sku;
310
private String name;
311
private double price;
312
private String category;
313
314
@Override
315
public int hashCode() {
316
return HashCodeBuilder.reflectionHashCode(this);
317
}
318
319
// Exclude specific fields
320
public int hashCodeIgnorePrice() {
321
return HashCodeBuilder.reflectionHashCode(this, "price");
322
}
323
324
// Custom initial values and multiplier
325
public int customHashCode() {
326
return HashCodeBuilder.reflectionHashCode(17, 37, this);
327
}
328
}
329
```
330
331
### CompareToBuilder - Fluent compareTo() Construction
332
333
Creates Comparable implementations with proper null handling:
334
335
```java { .api }
336
import org.apache.commons.lang3.builder.CompareToBuilder;
337
```
338
339
#### Basic CompareToBuilder Usage
340
341
```java { .api }
342
public class Person implements Comparable<Person> {
343
private String lastName;
344
private String firstName;
345
private int age;
346
private Date birthDate;
347
348
@Override
349
public int compareTo(Person other) {
350
return new CompareToBuilder()
351
.append(lastName, other.lastName)
352
.append(firstName, other.firstName)
353
.append(age, other.age)
354
.append(birthDate, other.birthDate)
355
.toComparison();
356
}
357
}
358
```
359
360
#### CompareToBuilder Methods
361
362
```java { .api }
363
// Basic field comparison
364
public CompareToBuilder append(Object lhs, Object rhs)
365
public CompareToBuilder append(Object lhs, Object rhs, Comparator<?> comparator)
366
public CompareToBuilder append(boolean lhs, boolean rhs)
367
public CompareToBuilder append(byte lhs, byte rhs)
368
public CompareToBuilder append(char lhs, char rhs)
369
public CompareToBuilder append(double lhs, double rhs)
370
public CompareToBuilder append(float lhs, float rhs)
371
public CompareToBuilder append(int lhs, int rhs)
372
public CompareToBuilder append(long lhs, long rhs)
373
public CompareToBuilder append(short lhs, short rhs)
374
375
// Array comparison
376
public CompareToBuilder append(Object[] lhs, Object[] rhs)
377
public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator<?> comparator)
378
// ... similar for other arrays
379
380
// Advanced comparison
381
public CompareToBuilder appendSuper(int superCompareTo)
382
public int toComparison()
383
```
384
385
#### Reflection-Based compareTo()
386
387
```java { .api }
388
public class Employee implements Comparable<Employee> {
389
private String department;
390
private String name;
391
private int level;
392
private double salary;
393
394
@Override
395
public int compareTo(Employee other) {
396
return CompareToBuilder.reflectionCompare(this, other);
397
}
398
399
// Exclude specific fields from comparison
400
public int compareIgnoreSalary(Employee other) {
401
return CompareToBuilder.reflectionCompare(this, other, "salary");
402
}
403
404
// Custom field order
405
public int compareCustomOrder(Employee other) {
406
return new CompareToBuilder()
407
.append(department, other.department) // Primary sort
408
.append(level, other.level) // Secondary sort
409
.append(name, other.name) // Tertiary sort
410
.toComparison();
411
}
412
}
413
```
414
415
## Advanced Builder Patterns
416
417
### Custom ToStringStyle
418
419
```java { .api }
420
public class CustomToStringStyle extends ToStringStyle {
421
422
public CustomToStringStyle() {
423
super();
424
this.setUseShortClassName(true);
425
this.setUseIdentityHashCode(false);
426
this.setFieldSeparator(" | ");
427
this.setFieldNameValueSeparator(": ");
428
this.setContentStart("[");
429
this.setContentEnd("]");
430
this.setNullText("NULL");
431
this.setArrayStart("{");
432
this.setArrayEnd("}");
433
this.setArraySeparator(", ");
434
}
435
}
436
437
// Usage
438
public String customToString() {
439
return new ToStringBuilder(this, new CustomToStringStyle())
440
.append("field1", value1)
441
.append("field2", value2)
442
.toString();
443
}
444
// Output: ClassName[field1: value1 | field2: value2]
445
```
446
447
### Builder Annotations
448
449
```java { .api }
450
import org.apache.commons.lang3.builder.ToStringExclude;
451
import org.apache.commons.lang3.builder.EqualsExclude;
452
import org.apache.commons.lang3.builder.HashCodeExclude;
453
454
public class User {
455
private String username;
456
private String email;
457
458
@ToStringExclude
459
@EqualsExclude
460
@HashCodeExclude
461
private String password; // Excluded from toString, equals, and hashCode
462
463
private Date lastLogin;
464
465
@Override
466
public String toString() {
467
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
468
// password field will be automatically excluded
469
}
470
471
@Override
472
public boolean equals(Object obj) {
473
return EqualsBuilder.reflectionEquals(this, obj);
474
// password field will be automatically excluded
475
}
476
477
@Override
478
public int hashCode() {
479
return HashCodeBuilder.reflectionHashCode(this);
480
// password field will be automatically excluded
481
}
482
}
483
```
484
485
### Complete Entity Example
486
487
```java { .api }
488
public class Order implements Comparable<Order> {
489
private Long id;
490
private String orderNumber;
491
private Date orderDate;
492
private BigDecimal totalAmount;
493
private OrderStatus status;
494
private Customer customer;
495
private List<OrderItem> items;
496
497
// Constructor, getters, setters...
498
499
@Override
500
public boolean equals(Object obj) {
501
if (this == obj) return true;
502
if (obj == null || getClass() != obj.getClass()) return false;
503
504
Order order = (Order) obj;
505
506
return new EqualsBuilder()
507
.append(id, order.id)
508
.append(orderNumber, order.orderNumber)
509
.isEquals();
510
}
511
512
@Override
513
public int hashCode() {
514
return new HashCodeBuilder(17, 37)
515
.append(id)
516
.append(orderNumber)
517
.toHashCode();
518
}
519
520
@Override
521
public String toString() {
522
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
523
.append("id", id)
524
.append("orderNumber", orderNumber)
525
.append("orderDate", orderDate)
526
.append("totalAmount", totalAmount)
527
.append("status", status)
528
.append("customerName", customer != null ? customer.getName() : null)
529
.append("itemCount", items != null ? items.size() : 0)
530
.toString();
531
}
532
533
@Override
534
public int compareTo(Order other) {
535
return new CompareToBuilder()
536
.append(orderDate, other.orderDate) // Primary: newest first
537
.append(id, other.id) // Secondary: by ID
538
.toComparison();
539
}
540
541
// Debug toString with all details
542
public String toDetailedString() {
543
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
544
.append("id", id)
545
.append("orderNumber", orderNumber)
546
.append("orderDate", orderDate)
547
.append("totalAmount", totalAmount)
548
.append("status", status)
549
.append("customer", customer)
550
.append("items", items)
551
.toString();
552
}
553
}
554
```
555
556
## Builder Utilities Integration
557
558
### Factory Methods for Builders
559
560
```java { .api }
561
public final class BuilderUtils {
562
563
// Fluent builder creation
564
public static ToStringBuilder toStringBuilder(Object object) {
565
return new ToStringBuilder(object, ToStringStyle.SHORT_PREFIX_STYLE);
566
}
567
568
public static ToStringBuilder jsonBuilder(Object object) {
569
return new ToStringBuilder(object, ToStringStyle.JSON_STYLE);
570
}
571
572
public static EqualsBuilder equalsBuilder() {
573
return new EqualsBuilder();
574
}
575
576
public static HashCodeBuilder hashCodeBuilder() {
577
return new HashCodeBuilder(17, 37);
578
}
579
580
public static CompareToBuilder compareToBuilder() {
581
return new CompareToBuilder();
582
}
583
584
// Null-safe reflection methods
585
public static String safeToString(Object obj) {
586
return obj != null ? ReflectionToStringBuilder.toString(obj) : "null";
587
}
588
589
public static boolean safeEquals(Object obj1, Object obj2) {
590
if (obj1 == obj2) return true;
591
if (obj1 == null || obj2 == null) return false;
592
if (obj1.getClass() != obj2.getClass()) return false;
593
594
return EqualsBuilder.reflectionEquals(obj1, obj2);
595
}
596
597
public static int safeHashCode(Object obj) {
598
return obj != null ? HashCodeBuilder.reflectionHashCode(obj) : 0;
599
}
600
}
601
```
602
603
### Validation with Builders
604
605
```java { .api }
606
@Component
607
public class EntityValidator {
608
609
public <T> void validateEqualsHashCode(T obj1, T obj2) {
610
boolean equals = obj1.equals(obj2);
611
boolean hashCodesEqual = obj1.hashCode() == obj2.hashCode();
612
613
if (equals && !hashCodesEqual) {
614
throw new IllegalStateException(
615
"Objects are equal but have different hash codes: " +
616
"obj1.hashCode()=" + obj1.hashCode() +
617
", obj2.hashCode()=" + obj2.hashCode());
618
}
619
620
// Log for debugging
621
log.debug("Validation passed for objects: {}, {}",
622
safeToString(obj1), safeToString(obj2));
623
}
624
625
public <T extends Comparable<T>> void validateComparable(T obj1, T obj2) {
626
int comparison = obj1.compareTo(obj2);
627
boolean equals = obj1.equals(obj2);
628
629
if (comparison == 0 && !equals) {
630
throw new IllegalStateException(
631
"Objects compare as equal but are not equal: " +
632
safeToString(obj1) + " vs " + safeToString(obj2));
633
}
634
635
if (comparison != 0 && equals) {
636
throw new IllegalStateException(
637
"Objects are equal but don't compare as equal: " +
638
safeToString(obj1) + " vs " + safeToString(obj2));
639
}
640
}
641
}
642
```
643
644
### Performance Considerations
645
646
```java { .api }
647
public class OptimizedEntity {
648
private final String id;
649
private final String name;
650
private final int value;
651
652
// Cache hash code for immutable objects
653
private transient int hashCode = 0;
654
655
public OptimizedEntity(String id, String name, int value) {
656
this.id = id;
657
this.name = name;
658
this.value = value;
659
}
660
661
@Override
662
public boolean equals(Object obj) {
663
// Fast path for same reference
664
if (this == obj) return true;
665
if (obj == null || getClass() != obj.getClass()) return false;
666
667
OptimizedEntity other = (OptimizedEntity) obj;
668
669
// Most discriminating field first
670
return new EqualsBuilder()
671
.append(id, other.id) // Usually unique
672
.append(value, other.value) // Numeric comparison is fast
673
.append(name, other.name) // String comparison last
674
.isEquals();
675
}
676
677
@Override
678
public int hashCode() {
679
// Cache hash code for immutable objects
680
if (hashCode == 0) {
681
hashCode = new HashCodeBuilder(17, 37)
682
.append(id)
683
.append(name)
684
.append(value)
685
.toHashCode();
686
}
687
return hashCode;
688
}
689
690
@Override
691
public String toString() {
692
// Use simple concatenation for performance-critical paths
693
return getClass().getSimpleName() + "[id=" + id + ",name=" + name + ",value=" + value + "]";
694
}
695
}
696
```
697
698
The builder utilities in Apache Commons Lang provide a robust foundation for implementing the essential Object methods with proper null safety, array handling, and customizable formatting, reducing boilerplate code while ensuring correctness and consistency.