Encapsulation and Information Hiding

Look again at DynamicArray. The data (arr, size) and the operations on that data (add, get, grow) live together inside one class. Bundling data with the operations that act on it is called encapsulation.

This is what lets size stay equal to the number of elements the client has added. add is the only operation that increases it, grow is the only one that changes the capacity, and both are updated by the same methods that write to arr. size and the capacity cannot become inconsistent with the elements stored in arr because no code outside the class changes them.

Information hiding

The private keyword on arr and size means code outside the class cannot read or change those fields directly. This is the information hiding principle: keep the internal representation hidden, and let clients interact only through methods you expose.

Our class relies on an invariant, which is a statement that has to stay true for the object to make sense. Ours is 0 <= size <= arr.length. As long as that holds, every other method can assume the elements sit in slots 0 through size - 1.

Suppose a client could write a.size = 100 on a dynamic array holding three elements in a ten-slot array. Nothing was added, so slots 3 through 9 still hold 0 and slots 10 through 99 do not exist. Now size() reports 100, get(50) throws an “index out of bounds” exception, and the next add tries to write to arr[100] and throws the same exception. Each of those methods is correct on its own, and one assignment from outside broke all of them. If the fields are private, the only way to change the object is to call a method, and every method keeps the invariant true.

It also gives us a safe way to offer new operations. Suppose we want to let clients overwrite an existing element. We add a public method for it:

public void set(int index, int value) {
  if (index < 0 || index >= size) {
    throw new IndexOutOfBoundsException();
  }
  arr[index] = value;
}

set lets the client change a value at a valid index, and the client still never touches the raw array. Note the check is index >= size, not index >= arr.length. A slot past size exists in the backing array, but it is not an element of the collection, and writing there would store a value in a slot that no other method reads. Note also that set overwrites an element that already exists, so size does not change. Growing the collection is still done by add, and set only changes an element that already exists.

Our get from the earlier note should carry the same check, for the same reason. As written it returns whatever is in arr[index], which for an index past size is an unused slot rather than an element the client added. Adding the same three-line guard to get fixes it.

We decide which operations clients get, and we check that each one leaves the invariant true.