An immutable object has no changeable observable state after its object constructor finishes. Two clients may hold references to the same immutable object, but neither can change what the other observes. Thus sharing an immutable object avoids the interference normally associated with reference aliasing: for example, sharing a Java
String is safe because a text-changing operation returns another software object rather than modifying that String data type value.A
final reference alone does not establish an immutable object: it prevents reassignment of that reference, but its referent could still be mutable. An immutable object must also protect any reachable mutable state, for instance through defensive copying. With that qualification, the essential answer isTake the lower coordinate in each direction from the smaller lower bound, and the upper coordinate from the larger upper bound. This gives an axis-aligned bounding box, rather than the generally nonrectangular set union of the two regions. Use defensive copying because the paper's
Point has mutable fields.final class Rect {
private final Point lowerLeft, upperRight;
public Rect(Point ll, Point ur) {
if (ll == null || ur == null)
throw new IllegalArgumentException("null corner");
double xl = ll.getX(), yl = ll.getY();
double xh = ur.getX(), yh = ur.getY();
if (!Double.isFinite(xl) || !Double.isFinite(yl)
|| !Double.isFinite(xh) || !Double.isFinite(yh)
|| xl > xh || yl > yh)
throw new IllegalArgumentException("invalid rectangle");
lowerLeft = new Point(xl, yl);
upperRight = new Point(xh, yh);
}
public Rect Union(Rect r) {
return new Rect(
new Point(Math.min(lowerLeft.getX(), r.lowerLeft.getX()),
Math.min(lowerLeft.getY(), r.lowerLeft.getY())),
new Point(Math.max(upperRight.getX(), r.upperRight.getX()),
Math.max(upperRight.getY(), r.upperRight.getY())));
}
public Point lowerLeft() { return lowerLeft.clone(); }
public Point upperRight() { return upperRight.clone(); }
}Any containing rectangle must reach at least as far as each input rectangle in all four coordinate directions. The constructed rectangle reaches exactly those four limits, which proves both containment and minimality:The corner accessors return copies, so changing a constructor argument or returned
Point cannot alter an existing Rect.Make defensive copies on both entry and exit, while keeping the supplied The private stored dimensions have no aliases exposed to the caller and are never changed. Mutating the constructor argument affects only the caller's object; mutating a returned
Dimension class unchanged:class Frame(d: Dimension) {
require(d != null)
private val saved = new Dimension(d.width, d.height)
private val savedArea = saved.width * saved.height
def area: Int = savedArea
def dimension: Dimension = new Dimension(saved.width, saved.height)
def isPortrait: Boolean = saved.height >= saved.width
}Dimension affects only a fresh copy. Consequently the stored area is calculated once and stays equal to the stored dimensions' product. This defensive copying is sufficient because Dimension contains only primitive fields; a more deeply nested mutable structure could require a deep copy or an immutable representation.The multiplication above deliberately retains the supplied class's
Int arithmetic and accessor type. If an exact mathematical area outside that range is required, compute once with saved.width.toLong * saved.height.toLong and expose Long instead; avoiding aliases and avoiding integer overflow are separate concerns.