Value Objects

Goals

Concepts

Library

Preview

import java.util.Objects;

/**
 * Represents a two-dimensional geometrical point.
 * <p>Points are considered equal if their X and Y coordinates are equal.</p>
 */
public final class Point {

  /** The point at coordinates (0, 0). */
  public final static Point ORIGIN = new Point(0, 0);

  private final int x;

  /** @return The X coordinate. */
  public int getX() {
    return x;
  }

  private final int y;

  /** @return The Y coordinate. */
  public int getY() {
    return y;
  }

  /**
   * X and Y coordinates constructor.
   * @param x The X coordinate.
   * @param y The Y coordinate.
   */
  private Point(final int x, final int y) {
    this.x = x;
    this.y = y;
  }

  /**
   * Returns a point with the given coordinates.
   * @param x The X coordinate.
   * @param y The Y coordinate.
   * @return A point with the indicated coordinates.
   */
  public static Point of(final int x, final int y) {
    if(x == 0 && y == 0) {
      return ORIGIN;
    }
    return new Point(x, y);
  }

  /**
   * Performs a translation operation, returning a point in a different relative position.
   * @param translateX The horizontal distance to move.
   * @param translateY The vertical distance to move.
   * @return A point in the new translated position.
   */
  public static Point translate(final int translateX, final int translateY) {
    return Point.of(getX() + translateX, getY() + translateY);
  }

  /**
   * Performs a scale operation, returning a point in a different scaled position.
   * @param scaleX The horizontal amount to scale.
   * @param scaleY The vertical amount to scale.
   * @return A point in the new scaled position.
   */
  public static Point scale(final double scaleX, final double scaleY) {
    return Point.of((int)(getX() * scaleX), (int)(getY() * scaleY));
  }

  @Override
  public boolean equals(final Object object) {
    if(this == object) {  //identical objects are always equal
      return true;
    }
    if(!(object instanceof Point)) {  //a Point can only be equal to another Point
      return false;
    }
    final Point point = (Point)object;
    return this.getX() == point.getX() && this.getY() == point.getY();
  }

  @Override
  public int hashCode() {
    return Objects.hash(getX(), getY());
  }

  @Override
  public String toString() {
    return String.format("(%s, %s)", getX(), getY());
  }
}

Lesson

When object-oriented programming first came out, developers began creating complex hierarchies of types. Each class had many public methods that allowed instances not only to do things but also to change their internal information. Changing the state of an object was a simple as as calling one of its methods. But because these objects could be changed, a module with a reference to an object in one part of a system might, simply by calling an object's method's method, make changes that would come as a surprise to other parts of the system.

For some classes representing business objects, such as a bank account, it is natural to allow access from multiple sources; it is in part the responsibility of the bank account class to coordinate this access. But what about a simple value such as five dollars, or the ninth of September? It would come as a surprise to someone depositing money of the amount of cash in their hand suddenly changed from five dollars to eight rupees, but this is exactly what could happen if the money value class allowed callers to change the money and currency represented.

Thus it has recently become clear the benefits of creating value objects to represent simple values (even if they contain multiple instance variables) that are interchangeable if their represented values are interchangeable. Take for instance a class to represent a calendar date. The class might contain a year, month, and day of the month. Once created, no one would be able to change the date it represented. You could create various date instances, and if they contained the same values (year, month, day) they would be considered an equivalent date (even if they were not the same instance).

Here are examples of small bundles of values that might be best represented by value objects. What internal values might each of these value objects hold?

Famous Value Objects

Before, discussing best practices of making your own value objects, it is instructive to see what value objects already exist for your use.

Primitive Wrappers

You've learned about and grown to love Java's primitive types: byte, short, int, long, float, double, boolean, and char. You know that these types are the only ones that do not reference objects, and thus cannot be null. But what if you have some method that works for any object, and you'd like to pass in a value from one of these primitive types? Wouldn't it be possible to create a class that did nothing else but hold a single primitive value?

Indeed Java has created such wrapper classes for all of the primitive types, so called because they conceptually wrap around the primitive value. You've already seen one of these classes, java.lang.Double, when you used the constant Double.POSITIVE_INFINITY, but the class Double class does more than just provide constants and utility methods; it can be instantiated to represent a single double value. (More on how to create an instance of Double can be found below under Static Factory Methods.) Here are the primitive types and corresponding wrapper classes provided by Java.

Primitive Type Wrapper Class
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
boolean java.lang.Boolean
char java.lang.Character
Numbers

One benefit of having a class wrap a primitive type is that classes can have inheritance hierarchies. Java has provided a java.lang.Number class which serves as a base class for all the wrapper classes representing primitive types that are numbers.

Number class diagram
Class diagram of Java number wrapper classes.

All java.lang.Number instances, regardless of subtype, will have a longValue() method for returning their value as a primitive long.

Boxing

Over the years Java has offered various conveniences to developers, many of which were no more than syntactic sugar, adding no real capabilities but making it easier to accomplish something already possible. One such feature, which on the surface seems very useful, is autoboxing. Creating a wrapper class instance (such as Integer) to represent a primitive value (such as 5) is called boxing. Converting that object back to a primitive value (such as Integer.intValue() is called unboxing. For a while now Java has had the ability to make this conversion automatically, without the developer explicitly requesting it.

Consider the case of a method that takes an Integer as a parameter. If you attempt to invoke that method using the value 5, Java will convert that value to an instance of Integer without being asked! Likewise, if the method in question uses that Integer instance in an expression, Java will convert the reference back to a primitive value on the fly.

Automatic boxing and unboxing.
/**
 * Calculates the royalties due for a book published in a particular year.
 * @param yearPublished The year the book was published,
 *     or {@code null} if the book has not yet been published.
 * @return The amount of royalties due, in dollars.
 * @see #getRoyaltyRate()
 * @see #getCurrentYear()
 */
public static int calculateRoyalties(@Nullable final Integer yearPublished) {
  if(yearPublished == null) {  //if the book is not published
    return 0;  //no royalties due
  }
  return getRoyaltyRate() * (getCurrentYear() - yearPublished); //auto-unboxing
}
final Integer royaltiesDue = calculateRoyalties(2005); //autoboxing
Big Numbers

Along with the primitive wrapper classes Java provides two classes for representing extremely large numbers, as you might have noted from the Numbers class diagram above. Integral values that are larger than Long supports can be stored in java.math.BigInteger, while non-integral values larger than Double supports can be stored in java.math.BigDecimal—and with more precision as well.

Strings

A value object you've already worked with is java.lang.String. Although you haven't used the new keyword to create them, Java creates value objects for the string literals you create.

Worst Practices

When Java was first released, many of its complex data types stored data in a publicly modifiable class that was little more than a structure in the C programming language. A good example is the java.awt.Insets class from the Java AWT, which provided public bottom, left, right, and top fields.

java.awt.Insets (abridged)
public class Insets {
  public int bottom;
  public int left;
  public int right;
  public int top;
}

In practice this had limitations:

Best Practices

Now that we know what not to do, let's take a look at some of the things we should do when making a class to represent a simple value. We'll be following the Java documentation's description of value-based classes, which generally follows accepted best practices.

Immutable Classes

One of the fundamental principles that has become evident in object-oriented programming is that making a class immutable, or unable to be changed (that is, the values it holds in its instance variables), makes a class and the program that uses it less prone to errors. If a program creates an instance of a class and then later makes some decision with the assumption that the object's value has not changed, the program will likely produce errors if in the meantime some other module has modified the object. Immutability prevents this from happening.

Immutability is especially relevant for value objects. You wouldn't want to make a calculation with the number 5, only to find out that half-way through the algorithm that the value 5 now was the value 10 without your knowledge. Value objects represent simple values; they simply may have more parts. Thus immutability is important to them as well.

Final Classes

Many value objects are defined in classes marked final, which prevents others from creating subclasses. If the classes weren't final, others could possibly extend the class and change the behavior. There are other ways to prevent others to override behavior such as marking methods as final, but for most simple value objects it's probably easiest and less confusing just to mark the classes themselves as final.

Static Factory Methods

Another common trait of value objects is that they provide special constant methods the sole purpose of which is to create instances of the class! We call these methods factory methods because they function like a factory that produces objects on demand. You already used static factory methods in the lesson on generics.

You might think that such a method would be unnecessary and even redundant—after all, isn't that what a constructor is for? But it turns out that, for value classes, having a separate static method for object creation provides much more control than a constructor. Here are some of the benefits of a static factory method:

The primitive wrapper classes provide excellent examples of static factory methods named valueOf(…). The java.lang.Integer.valueOf(int) method as one example keeps a cache of pre-constructed Integer instances. If the caller requests a wrapper instance matching one that is already in the cache, the existing instance can be returned rather than creating a new one.

java.lang.Integer.valueOf(int) (abridged)
public final class Integer {

  public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high) {
      return IntegerCache.cache[i + (-IntegerCache.low)];
    }
    return new Integer(i);
  }

  …
final Integer value = Integer.valueOf(5);

If a caller were to invoke new Integer(5) multiple times, multiple instances of Integer would be created, even though they all hold the same primitive value. Yet if a caller invokes Integer.valueOf(5) multiple times, each time the same instance of Integer would be returned, with no need to create a new one. This not only saves time, it also reduces memory usage.

Fluent Value Manipulation Methods

Because they are immutable, the values stored by value objects can't be modified as such. But it is typical that one might want to manipulate that value to produce some other value (commonly an other instance of the same value class). The java.lang.BigInteger class, as it may contain values too large to be manipulated via a primitive value, has special methods for adding and multiplying (among other operations) the current BigInteger instance with another.

java.math.BigInteger (abridged)
public class BigInteger {

  public BigInteger add(BigInteger val) { … }

  public BigInteger multiply(BigInteger val) { … }

  …

As the result is itself a BigInteger, additional methods can be chained to further manipulate the value, producing a sequence of fluent methods that resembles English. Thus arithmetic with BigDecimal can performed like this:

final BigInteger result = BigInteger.valueOf(5).multiply(BigInteger.valueOf(2)).add(BigInteger.ONE);

Equality

Value objects are simple, immutable objects representing a single value. Because they are immutable, they can thus be interchangeable! For this to function correctly, instances that hold equivalent objects must be recognized as equal—after all, calculations would not work if somehow 5 was not equal to 5. But because different instances could have been created to represent the same values, you cannot rely on == to compare them. Instead, you must override the Object.equals(Object) so that they can be compared using fooBar1.equals(fooBar2).

The equals(…) method is fundamentally important, and works like this: it takes a single argument, which may be null, and returns true or false depending on whether the argument is equal to the object on which the method is being invoked. What constitutes equal?

What constitutes as equal depends on the semantics of your class, but there are a few common-sense guideposts:

Let's look how equals might be implemented on a Point value object, which stores two-dimensional geometric coordinates.

Point.equals(…)
@Override
public boolean equals(final Object object) {
  if(this == object) {  //identical objects are always equal
    return true;
  }
  if(!(object instanceof Point)) {  //a Point can only be equal to another Point
    return false;
  }
  final Point point = (Point)object;
  return this.getX() == point.getX() && this.getY() == point.getY();
}

Hash Code

A hash code in Java is an arbitrary number assigned to an object to speed up comparison. Every class that overrides the equals(…)method must also override the Object.hashCode() method—even those classes that do not represent value objects. The hashCode() method is used throughout Java as a way to make comparing objects more efficient.

As you learned in the lesson on hash tables, a hash code allows you group objects into buckets based upon their hash codes. When it's time to retrieve the object, the hash table needs merely to look in the bucket with the same number as the hash code. Each item in the bucket will still need to be compared via equals(…), but the search will be narrowed down considerably.

There are some common-sense implications of this:

Hash Codes for Single Values

A good hash code must be the same for equal objects, and should be different for non-equal objects if possible. So how does one determine a good hash code? It all depends on what the underlying values are relating to equality.

If there is only one value that determine equality, the answer is simple: return the hash code of that object! For example, imagine a value object representing the postal code of some country (referred to as a zip code in the United States). This class would simply wrap a string value, bringing the benefit of type safety while providing added semantics. As Strings themselves override hashCode() and follow its contract, and as PostalCode's equality is based upon this single string, PostalCode.hashCode() can simply return the hash code of the internal string value. We say that PostalCode.hashCode() delegates to String.hashCode().

PostalCode.hashCode()
public final class PostalCode {

  private final String code;

  …

  @Override
  public int hashCode() {
    return code.hashCode();
  }
}
Hash Codes for Multiple Values

If your value object (or any object) considers equality based upon more than one value, you must find a way to return a single hash code value that somehow represents those underlying values while still following the contract of Object.hashCode(). If you search the web you will find various formulas which purport to combine multiple values into a single, good hash code. But nowadays there is no need to roll your own hash code algorithm; Java provides the java.lang.Objects.hash(Object...) method to calculate a hash code based upon as many values as you provide. Let's make a Point.hashCode() method to go along with our Point.equals(…) method above:

Point.hashCode()
@Override
public int hashCode() {
  return java.util.Objects.hash(getX(), getY());
}

Review

Summary

For simple values stored in classes, follow best practices in defining classes for these value objects:

Gotchas

In the Real World

Think About It

Self Evaluation

Task

Some of the books shown in the Booker application have a range of years for the first published date. Create a value object class that represents a range of years, and integrate that value object into your periodical hierarchy at the appropriate place(s).

See Also

References

Acknowledgments