Apache Commons Lang provides essential Java utility classes for string manipulation, object operations, array handling, date/time processing, reflection utilities, and more.
—
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.
Creates readable string representations of objects with extensive customization options:
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;public class Person {
private String name;
private int age;
private List<String> hobbies;
@Override
public String toString() {
return new ToStringBuilder(this)
.append("name", name)
.append("age", age)
.append("hobbies", hobbies)
.toString();
}
}
// Output: Person@1a2b3c4d[name=John Doe,age=25,hobbies=[reading, gaming]]// Basic field appending
public ToStringBuilder append(String fieldName, Object obj)
public ToStringBuilder append(String fieldName, boolean value)
public ToStringBuilder append(String fieldName, byte value)
public ToStringBuilder append(String fieldName, char value)
public ToStringBuilder append(String fieldName, double value)
public ToStringBuilder append(String fieldName, float value)
public ToStringBuilder append(String fieldName, int value)
public ToStringBuilder append(String fieldName, long value)
public ToStringBuilder append(String fieldName, short value)
// Array appending
public ToStringBuilder append(String fieldName, Object[] array)
public ToStringBuilder append(String fieldName, boolean[] array)
public ToStringBuilder append(String fieldName, byte[] array)
// ... similar for other primitive arrays
// Advanced appending
public ToStringBuilder appendAsObjectToString(Object object)
public ToStringBuilder appendSuper(String superToString)
public ToStringBuilder appendToString(String toString)// Predefined styles
ToStringStyle.DEFAULT_STYLE // ClassName@hashcode[field=value,field=value]
ToStringStyle.MULTI_LINE_STYLE // Each field on separate line
ToStringStyle.NO_FIELD_NAMES_STYLE // Values without field names
ToStringStyle.SHORT_PREFIX_STYLE // ClassName[field=value,field=value]
ToStringStyle.SIMPLE_STYLE // field=value,field=value
ToStringStyle.NO_CLASS_NAME_STYLE // [field=value,field=value]
ToStringStyle.JSON_STYLE // {"field":"value","field":"value"}Usage Examples:
public class Employee {
private String name = "John Doe";
private int id = 12345;
private String[] skills = {"Java", "Python", "SQL"};
private Address address = new Address("123 Main St", "New York");
// Default style
public String toString() {
return new ToStringBuilder(this)
.append("name", name)
.append("id", id)
.append("skills", skills)
.append("address", address)
.toString();
}
// Output: Employee@1a2b3c4d[name=John Doe,id=12345,skills={Java,Python,SQL},address=Address@5f6g7h8i[...]]
// JSON style
public String toJsonString() {
return new ToStringBuilder(this, ToStringStyle.JSON_STYLE)
.append("name", name)
.append("id", id)
.append("skills", skills)
.toString();
}
// Output: {"name":"John Doe","id":12345,"skills":["Java","Python","SQL"]}
// Multi-line style
public String toMultiLineString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("name", name)
.append("id", id)
.append("skills", skills)
.toString();
}
// Output:
// Employee@1a2b3c4d[
// name=John Doe
// id=12345
// skills={Java,Python,SQL}
// ]
// No field names style
public String toSimpleString() {
return new ToStringBuilder(this, ToStringStyle.NO_FIELD_NAMES_STYLE)
.append(name)
.append(id)
.append(skills)
.toString();
}
// Output: Employee@1a2b3c4d[John Doe,12345,{Java,Python,SQL}]
}Automatically generates toString() using reflection:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
public class Product {
private String name = "Laptop";
private double price = 999.99;
private boolean inStock = true;
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
// Automatically includes all fields
// With custom style
public String toJsonString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE);
}
// Exclude specific fields
public String toStringExcludePrice() {
return ReflectionToStringBuilder.toStringExclude(this, "price");
}
// Include only specific fields
public String toStringOnlyName() {
return ReflectionToStringBuilder.toStringInclude(this, "name");
}
}Creates robust equals() methods with null safety and type checking:
import org.apache.commons.lang3.builder.EqualsBuilder;public class Person {
private String firstName;
private String lastName;
private int age;
private Date birthDate;
private String[] hobbies;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return new EqualsBuilder()
.append(firstName, person.firstName)
.append(lastName, person.lastName)
.append(age, person.age)
.append(birthDate, person.birthDate)
.append(hobbies, person.hobbies)
.isEquals();
}
}// Basic field comparison
public EqualsBuilder append(Object lhs, Object rhs)
public EqualsBuilder append(boolean lhs, boolean rhs)
public EqualsBuilder append(byte lhs, byte rhs)
public EqualsBuilder append(char lhs, char rhs)
public EqualsBuilder append(double lhs, double rhs)
public EqualsBuilder append(float lhs, float rhs)
public EqualsBuilder append(int lhs, int rhs)
public EqualsBuilder append(long lhs, long rhs)
public EqualsBuilder append(short lhs, short rhs)
// Array comparison
public EqualsBuilder append(Object[] lhs, Object[] rhs)
public EqualsBuilder append(boolean[] lhs, boolean[] rhs)
// ... similar for other primitive arrays
// Advanced comparison
public EqualsBuilder appendSuper(boolean superEquals)
public boolean isEquals()public class Employee {
private String id;
private String name;
private String department;
private double salary;
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
// Exclude specific fields from comparison
public boolean equalsIgnoreSalary(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj, "salary");
}
// Include only up to a certain class in hierarchy
public boolean equalsShallow(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj, false, Employee.class);
}
}Creates consistent hashCode() methods that work correctly with equals():
import org.apache.commons.lang3.builder.HashCodeBuilder;public class Person {
private String firstName;
private String lastName;
private int age;
private Date birthDate;
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37) // Two different odd prime numbers
.append(firstName)
.append(lastName)
.append(age)
.append(birthDate)
.toHashCode();
}
}// Basic field hashing
public HashCodeBuilder append(Object object)
public HashCodeBuilder append(boolean value)
public HashCodeBuilder append(byte value)
public HashCodeBuilder append(char value)
public HashCodeBuilder append(double value)
public HashCodeBuilder append(float value)
public HashCodeBuilder append(int value)
public HashCodeBuilder append(long value)
public HashCodeBuilder append(short value)
// Array hashing
public HashCodeBuilder append(Object[] array)
public HashCodeBuilder append(boolean[] array)
// ... similar for other primitive arrays
// Advanced hashing
public HashCodeBuilder appendSuper(int superHashCode)
public int toHashCode()public class Product {
private String sku;
private String name;
private double price;
private String category;
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
// Exclude specific fields
public int hashCodeIgnorePrice() {
return HashCodeBuilder.reflectionHashCode(this, "price");
}
// Custom initial values and multiplier
public int customHashCode() {
return HashCodeBuilder.reflectionHashCode(17, 37, this);
}
}Creates Comparable implementations with proper null handling:
import org.apache.commons.lang3.builder.CompareToBuilder;public class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int age;
private Date birthDate;
@Override
public int compareTo(Person other) {
return new CompareToBuilder()
.append(lastName, other.lastName)
.append(firstName, other.firstName)
.append(age, other.age)
.append(birthDate, other.birthDate)
.toComparison();
}
}// Basic field comparison
public CompareToBuilder append(Object lhs, Object rhs)
public CompareToBuilder append(Object lhs, Object rhs, Comparator<?> comparator)
public CompareToBuilder append(boolean lhs, boolean rhs)
public CompareToBuilder append(byte lhs, byte rhs)
public CompareToBuilder append(char lhs, char rhs)
public CompareToBuilder append(double lhs, double rhs)
public CompareToBuilder append(float lhs, float rhs)
public CompareToBuilder append(int lhs, int rhs)
public CompareToBuilder append(long lhs, long rhs)
public CompareToBuilder append(short lhs, short rhs)
// Array comparison
public CompareToBuilder append(Object[] lhs, Object[] rhs)
public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator<?> comparator)
// ... similar for other arrays
// Advanced comparison
public CompareToBuilder appendSuper(int superCompareTo)
public int toComparison()public class Employee implements Comparable<Employee> {
private String department;
private String name;
private int level;
private double salary;
@Override
public int compareTo(Employee other) {
return CompareToBuilder.reflectionCompare(this, other);
}
// Exclude specific fields from comparison
public int compareIgnoreSalary(Employee other) {
return CompareToBuilder.reflectionCompare(this, other, "salary");
}
// Custom field order
public int compareCustomOrder(Employee other) {
return new CompareToBuilder()
.append(department, other.department) // Primary sort
.append(level, other.level) // Secondary sort
.append(name, other.name) // Tertiary sort
.toComparison();
}
}public class CustomToStringStyle extends ToStringStyle {
public CustomToStringStyle() {
super();
this.setUseShortClassName(true);
this.setUseIdentityHashCode(false);
this.setFieldSeparator(" | ");
this.setFieldNameValueSeparator(": ");
this.setContentStart("[");
this.setContentEnd("]");
this.setNullText("NULL");
this.setArrayStart("{");
this.setArrayEnd("}");
this.setArraySeparator(", ");
}
}
// Usage
public String customToString() {
return new ToStringBuilder(this, new CustomToStringStyle())
.append("field1", value1)
.append("field2", value2)
.toString();
}
// Output: ClassName[field1: value1 | field2: value2]import org.apache.commons.lang3.builder.ToStringExclude;
import org.apache.commons.lang3.builder.EqualsExclude;
import org.apache.commons.lang3.builder.HashCodeExclude;
public class User {
private String username;
private String email;
@ToStringExclude
@EqualsExclude
@HashCodeExclude
private String password; // Excluded from toString, equals, and hashCode
private Date lastLogin;
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
// password field will be automatically excluded
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
// password field will be automatically excluded
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
// password field will be automatically excluded
}
}public class Order implements Comparable<Order> {
private Long id;
private String orderNumber;
private Date orderDate;
private BigDecimal totalAmount;
private OrderStatus status;
private Customer customer;
private List<OrderItem> items;
// Constructor, getters, setters...
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Order order = (Order) obj;
return new EqualsBuilder()
.append(id, order.id)
.append(orderNumber, order.orderNumber)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(id)
.append(orderNumber)
.toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("id", id)
.append("orderNumber", orderNumber)
.append("orderDate", orderDate)
.append("totalAmount", totalAmount)
.append("status", status)
.append("customerName", customer != null ? customer.getName() : null)
.append("itemCount", items != null ? items.size() : 0)
.toString();
}
@Override
public int compareTo(Order other) {
return new CompareToBuilder()
.append(orderDate, other.orderDate) // Primary: newest first
.append(id, other.id) // Secondary: by ID
.toComparison();
}
// Debug toString with all details
public String toDetailedString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", id)
.append("orderNumber", orderNumber)
.append("orderDate", orderDate)
.append("totalAmount", totalAmount)
.append("status", status)
.append("customer", customer)
.append("items", items)
.toString();
}
}public final class BuilderUtils {
// Fluent builder creation
public static ToStringBuilder toStringBuilder(Object object) {
return new ToStringBuilder(object, ToStringStyle.SHORT_PREFIX_STYLE);
}
public static ToStringBuilder jsonBuilder(Object object) {
return new ToStringBuilder(object, ToStringStyle.JSON_STYLE);
}
public static EqualsBuilder equalsBuilder() {
return new EqualsBuilder();
}
public static HashCodeBuilder hashCodeBuilder() {
return new HashCodeBuilder(17, 37);
}
public static CompareToBuilder compareToBuilder() {
return new CompareToBuilder();
}
// Null-safe reflection methods
public static String safeToString(Object obj) {
return obj != null ? ReflectionToStringBuilder.toString(obj) : "null";
}
public static boolean safeEquals(Object obj1, Object obj2) {
if (obj1 == obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (obj1.getClass() != obj2.getClass()) return false;
return EqualsBuilder.reflectionEquals(obj1, obj2);
}
public static int safeHashCode(Object obj) {
return obj != null ? HashCodeBuilder.reflectionHashCode(obj) : 0;
}
}@Component
public class EntityValidator {
public <T> void validateEqualsHashCode(T obj1, T obj2) {
boolean equals = obj1.equals(obj2);
boolean hashCodesEqual = obj1.hashCode() == obj2.hashCode();
if (equals && !hashCodesEqual) {
throw new IllegalStateException(
"Objects are equal but have different hash codes: " +
"obj1.hashCode()=" + obj1.hashCode() +
", obj2.hashCode()=" + obj2.hashCode());
}
// Log for debugging
log.debug("Validation passed for objects: {}, {}",
safeToString(obj1), safeToString(obj2));
}
public <T extends Comparable<T>> void validateComparable(T obj1, T obj2) {
int comparison = obj1.compareTo(obj2);
boolean equals = obj1.equals(obj2);
if (comparison == 0 && !equals) {
throw new IllegalStateException(
"Objects compare as equal but are not equal: " +
safeToString(obj1) + " vs " + safeToString(obj2));
}
if (comparison != 0 && equals) {
throw new IllegalStateException(
"Objects are equal but don't compare as equal: " +
safeToString(obj1) + " vs " + safeToString(obj2));
}
}
}public class OptimizedEntity {
private final String id;
private final String name;
private final int value;
// Cache hash code for immutable objects
private transient int hashCode = 0;
public OptimizedEntity(String id, String name, int value) {
this.id = id;
this.name = name;
this.value = value;
}
@Override
public boolean equals(Object obj) {
// Fast path for same reference
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
OptimizedEntity other = (OptimizedEntity) obj;
// Most discriminating field first
return new EqualsBuilder()
.append(id, other.id) // Usually unique
.append(value, other.value) // Numeric comparison is fast
.append(name, other.name) // String comparison last
.isEquals();
}
@Override
public int hashCode() {
// Cache hash code for immutable objects
if (hashCode == 0) {
hashCode = new HashCodeBuilder(17, 37)
.append(id)
.append(name)
.append(value)
.toHashCode();
}
return hashCode;
}
@Override
public String toString() {
// Use simple concatenation for performance-critical paths
return getClass().getSimpleName() + "[id=" + id + ",name=" + name + ",value=" + value + "]";
}
}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.
Install with Tessl CLI
npx tessl i tessl/maven-org-apache-commons--commons-lang3