Class ImmutableValueObject
java.lang.Object
dk.cloudcreate.essentials.immutable.ImmutableValueObject
- All Implemented Interfaces:
Immutable
The base type
The core feature set of
Example:
Immutability
The default implementation of
To ensure that
ImmutableValueObject supports creating immutable (i.e. an object where its values cannot change after object instantiation/creation) Value ObjectThe core feature set of
ImmutableValueObject is that it provides default implementations for toString(), equals(Object) and hashCode(),
but you're always free to override this and provide your own implementation.Example:
public class ImmutableOrder extends ImmutableValueObject {
public final OrderId orderId;
public final CustomerId customerId;
@Exclude.EqualsAndHashCode
public final Percentage percentage;
@Exclude.ToString
public final Money totalPrice;
public ImmutableOrder(OrderId orderId,
CustomerId customerId,
Percentage percentage,
EmailAddress email,
Money totalPrice) {
this.orderId = orderId;
this.customerId = customerId;
this.percentage = percentage;
this.totalPrice = totalPrice;
}
}
Value Object definition
A Value Object is defined by its property values and not by an identifier.
If two value objects, of the same type, have the same property values then they're considered to be equal.
var thisOrder = new ImmutableOrder(OrderId.of(123456789),
CustomerId.of("CustomerId1"),
Percentage.from("50%"),
Money.of("1234.56", CurrencyCode.DKK));
var thatOrder = new ImmutableOrder(OrderId.of(123456789),
CustomerId.of("CustomerId1"),
Percentage.from("50%"),
Money.of("1234.56", CurrencyCode.DKK));
assertThat(thisOrder).isEqualTo(thatOrder);
Immutability
The default implementation of
toString() and hashCode() relies upon the assumption that ALL non-transient instance fields are marked final to ensure that they cannot change after they have been assigned a value.To ensure that
toString() and hashCode() calculation only happens once, the ImmutableValueObject will cache the output of the first call to toString() and hashCode().
This also means that if a field isn't final or if the field type is a mutable type, such as List, Map, Set, etc. then you cannot reliably rely on the output of followup calls to toString() and
hashCode() as the fields used for calculation may have changed value.
See toString(), hashCode() and equals(Object) for the logic related to each operation.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionbooleanThe logic ofequals(Object)follows the standard approach where we don't accept subclasses of a type, we only accept the exact same type.
All fields without theExclude.EqualsAndHashCodeannotation with be included in the equals operation.
The fields are sorted alphabetically (ascending order) and values from the two objects being compared one by one.
As soon a difference in field values is identified the comparison is stopped (to avoid wasting CPU cycles) and the result is returned.inthashCode()All fields without theExclude.EqualsAndHashCodeannotation with be included the calculation of thehash-code.
The fields are sorted alphabetically (ascending order) and thehash-codewill be calculated in the order of the fields names.
The algorithm for calculating the hashcode follows the Objects#hash(Object...) method.toString()All fields without theExclude.ToStringannotation with be included in the output.
The fields are sorted alphabetically (ascending order) and then grouped into fields with and without value.
We will first output non-null fields (in alphabetically order) and finally null fields (in alphabetically order)
Example:
-
Constructor Details
-
ImmutableValueObject
public ImmutableValueObject()
-
-
Method Details
-
hashCode
public int hashCode()All fields without theExclude.EqualsAndHashCodeannotation with be included the calculation of thehash-code.
The fields are sorted alphabetically (ascending order) and thehash-codewill be calculated in the order of the fields names.
The algorithm for calculating the hashcode follows the Objects#hash(Object...) method. -
equals
The logic ofequals(Object)follows the standard approach where we don't accept subclasses of a type, we only accept the exact same type.
All fields without theExclude.EqualsAndHashCodeannotation with be included in the equals operation.
The fields are sorted alphabetically (ascending order) and values from the two objects being compared one by one.
As soon a difference in field values is identified the comparison is stopped (to avoid wasting CPU cycles) and the result is returned. -
toString
All fields without theExclude.ToStringannotation with be included in the output.
The fields are sorted alphabetically (ascending order) and then grouped into fields with and without value.
We will first output non-null fields (in alphabetically order) and finally null fields (in alphabetically order)
Example:
will returnpublic class ImmutableOrder extends ImmutableValueObject { public final OrderId orderId; public final CustomerId customerId; @Exclude.EqualsAndHashCode public final Percentage percentage; @Exclude.ToString public final Money totalPrice; public ImmutableOrder(OrderId orderId, CustomerId customerId, Percentage percentage, EmailAddress email, Money totalPrice) { this.orderId = orderId; this.customerId = customerId; this.percentage = percentage; this.totalPrice = totalPrice; } } new ImmutableOrder(OrderId.of(123456789), null, Percentage.of("50%), Money.of("1234.56", CurrencyCode.DKK) ) .toString()
ImmutableOrder { orderId: 123456789, percentage: 50.00%, customerId: null }
withcustomerIdlast (as it has a null value) and without thetotalPriceas it is excluded from being included in thetoString()result due to@Exclude.ToString
-