Skip to main content

Section 12.3 Standardizing Behavior: Interfaces and ADTs

In the previous section, we established the need for a dynamic collection, specifically a list, that can grow as needed, overcoming the fixed-size limitation of basic arrays. Our goal is to build such a list, which we’ll call ArrayList<T>.
Now, let’s consider a new challenge. Imagine you and several classmates are all asked to create a dynamic list class. You might create an add(item) method, but another classmate might name theirs insertElement(item), and a third might use append(item). Similarly, methods to check the number of items might be called getSize(), count(), or length(). While each list might work internally, this inconsistency creates a major problem: how can you write code that reliably uses any of these different list implementations? For instance, if you wanted to write a simple utility method like printAnyList( /* some list */ ), you wouldn’t know whether to call .getSize() or .count() to loop correctly! Code reuse becomes difficult, and combining different list implementations in a larger program turns into a confusing mess.
// --- Problem Illustration: Inconsistent List Implementations ---

// Hypothetical List Class 1
class MyListV1 {
    void add(String item) { /* ... */ }
    int getSize() { /* ... */ return 0;}
    String getAtIndex(int index) { /* ... */ return null; }
    // ... other methods ...
}

// Hypothetical List Class 2
class AnotherList {
    void insertItem(String data) { /* ... */ }
    int count() { /* ... */ return 0;}
    String retrieve(int pos) { /* ... */ return null; }
    // ... other DIFFERENT methods ...
}

// Now, try writing a reusable method to print ANY list...
class ListUtils {
    static void printAnyList( ??? list ) { // What type should the parameter be??
        System.out.print("[");
        // How many times to loop? Call list.getSize()? or list.count()?
        // ERROR: We don't know which method name exists!
        int size = ??? ; // Problem 1: Which size method?

        for (int i = 0; i < size; i++) {
            // How to get the element? list.getAtIndex(i)? or list.retrieve(i)?
            // ERROR: We don't know which retrieval method exists!
            String element = ??? ; // Problem 2: Which get method?
            System.out.print(element);
            if (i < size - 1) {
                System.out.print(", ");
            }
        }
        System.out.println("]");
        // This reusable method is impossible to write reliably without a standard contract!
    }
}
This is the problem that interfaces solve in object-oriented programming (See Chapter 7, Chapter 9). An interface acts as a contract or a blueprint for behavior. It defines a set of public method signatures (the method name, parameters, and return type) but provides no implementation for those methods (mostly; default methods are an exception we won’t focus on here). Think of it as a list of required job functions without specifying exactly how to perform them.
// Example structure of an interface definition
public interface SomeContract {
    // Method signatures define WHAT implementing classes MUST provide
    ReturnType methodName1(ParamType1 param1);
    boolean anotherMethod(ParamType2 param2, ParamType3 param3);
    // No method bodies { ... } here!
}
When a class declares that it implements an interface (e.g., public class MyList implements SomeContract), it makes a binding promise to the Java compiler: "I guarantee I will provide concrete implementations for methodName1 and anotherMethod, exactly as specified in the SomeContract interface." The compiler enforces this promise rigorously; if the class fails to provide an implementation for any method in the contract, a compile-time error occurs.
This concept of defining behavior separately from implementation is central to the idea of an Abstract Data Type (ADT). An ADT is a conceptual model that specifies the operations a data type must support (like add, remove, get, size for a list) and the expected behavior of those operations, without dictating *how* the data is stored or how the operations are implemented internally. The interface is the Java mechanism we use to formally define the contract specified by an ADT.
For our ArrayList<T> project, we will work with two ADT interfaces that are provided for you:
  1. CollectionADT<T>: This interface defines the most fundamental behaviors common to nearly all collections, such as:
    • Checking if the collection is empty (isEmpty()).
    • Getting the number of items (size()).
    • Checking if an item exists in the collection (contains(T item)).
    The <T> indicates this is a generic interface (See Chapter 10), meaning it can define a collection holding any reference type (like String, Player, or Integer).
  2. ListADT<T>: This interface extends CollectionADT<T> (meaning it includes all methods from CollectionADT) and adds operations specific to ordered lists, where elements have positions (indices). Examples include:
    • Adding an element at a specific index (add(int index, T item)).
    • Removing the element at a specific index (remove(int index)).
    • Getting the element at a specific index (get(int index)).
Connection to the Design Recipe: These interfaces (CollectionADT<T> and ListADT<T>) represent our formal specification. They embody parts of Step 0 (Understand & Restate) by defining the required operations, Step 1 (Data Definitions) at an abstract level by defining the *concept* of a generic List, and Step 2 (Method Signature & Purpose) by providing the exact method signatures and intended purpose (which you can explore via the Javadoc comments in the provided interface files once we discuss the project setup). Our task in the subsequent Design Recipe steps will be to fulfill this contract using a dynamic array approach.

Note 12.3.1. Java’s Standard Collection Interfaces.

It’s worth noting that the interfaces we are using (CollectionADT, ListADT) are simplified versions designed for this educational exercise. Java’s standard library (java.util) contains its own, more extensive interfaces that solve the same problem of standardization: java.util.Collection<E> and java.util.List<E>. The core concepts are the same: define a contract so that different implementations (java.util.ArrayList, java.util.LinkedList) can be used interchangeably where appropriate. You’ll study these standard interfaces and their implementations in depth in subsequent courses like Data Structures and Algorithms.
You have attempted of activities on this page.