Class ImmutableValueObject

java.lang.Object
dk.cloudcreate.essentials.immutable.ImmutableValueObject
All Implemented Interfaces:
Immutable

public abstract class ImmutableValueObject extends Object implements Immutable
The base type ImmutableValueObject supports creating immutable (i.e. an object where its values cannot change after object instantiation/creation) Value Object
The 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
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    equals(Object that)
    The logic of equals(Object) follows the standard approach where we don't accept subclasses of a type, we only accept the exact same type.
    All fields without the Exclude.EqualsAndHashCode annotation 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.
    int
    All fields without the Exclude.EqualsAndHashCode annotation with be included the calculation of the hash-code.
    The fields are sorted alphabetically (ascending order) and the hash-code will be calculated in the order of the fields names.
    The algorithm for calculating the hashcode follows the Objects#hash(Object...) method.
    All fields without the Exclude.ToString annotation 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:

    Methods inherited from class java.lang.Object

    clone, finalize, getClass, notify, notifyAll, wait, wait, wait
  • Constructor Details

    • ImmutableValueObject

      public ImmutableValueObject()
  • Method Details

    • hashCode

      public int hashCode()
      All fields without the Exclude.EqualsAndHashCode annotation with be included the calculation of the hash-code.
      The fields are sorted alphabetically (ascending order) and the hash-code will be calculated in the order of the fields names.
      The algorithm for calculating the hashcode follows the Objects#hash(Object...) method.
      Overrides:
      hashCode in class Object
      Returns:
      the calculated hashcode
      See Also:
    • equals

      public boolean equals(Object that)
      The logic of equals(Object) follows the standard approach where we don't accept subclasses of a type, we only accept the exact same type.
      All fields without the Exclude.EqualsAndHashCode annotation 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.
      Overrides:
      equals in class Object
      Parameters:
      that - the other object we're comparing our instance to
      Returns:
      true if this object is the same as the that argument otherwise false
      See Also:
    • toString

      public String toString()
      All fields without the Exclude.ToString annotation 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:
      
       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;
           }
       }
      
       new ImmutableOrder(OrderId.of(123456789),
                          null,
                          Percentage.of("50%),
                          Money.of("1234.56", CurrencyCode.DKK)
                         )
                         .toString()
       
      will return
      ImmutableOrder { orderId: 123456789, percentage: 50.00%, customerId: null }
      with customerId last (as it has a null value) and without the totalPrice as it is excluded from being included in the toString() result due to @Exclude.ToString
      Overrides:
      toString in class Object
      Returns:
      all fields and their values (see method description)
      See Also: