Review

In class we saw that each type of collection has an interface (e.g. List, Set) and various implementations that implement those interfaces (e.g. ArrayList/LinkedList for List and TreeSet/HashSet for Set). We use interfaces to make our code more general; for example we can write code that works with any Set rather than having to write it once to work for TreeSet and another time for HashSet.

In Java, interfaces are used to make the code more general by making a promise that any class that implements the interface is guaranteed to have those methods. In this reading, we will see how to write an interface and make your classes implement them.

Interfaces

Writing an interface

Writing an interface is very similar to writing a class, but you use the keyword interface instead of class.

For example, we are going to make an IntList interface that acts like Java’s List but to go along with our ArrayIntList. We’ll see next week another way to implement an IntList called a LinkedIntList.

public interface IntList {
    // TODO fill this in
}

Recall that an interface is a way of specifying “all classes that implement my interface are required to have these methods”. To require all classes that are IntLists to have an add method, we write the method header for the method we want without a body (since interfaces don’t provide implementations, only requirements). We would then write

public interface IntList {
    public void add(int value);  // notice ; instead of { } with method body
}

Now any class that claims to be an IntList must have a method that matches the signature add(int). There are many other methods we probably want to make sure anyone who is an IntList has, so we can add more method requirements as so:

public interface IntList {
    public void add(int value);
    public void add(int index, int value);
    public boolean contains(int value);
    public int get(int index);
    public void remove(int index);
    public int size();
}

Now that we have the IntList interface, we can write methods that work with it! Remember that if we have a variable of type IntList, we are allowed to call any of the methods required by the IntList interface on that variable since Java will enforce that any class that implements the interface will have all the required methods.

For example if we wanted to write a method that took an IntList and printed all the values in the list, we could write the following

public static void printList(IntList list) {
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

Now let’s try calling the printList method.

public static void main(String[] args) {
    ArrayIntList list = new ArrayIntList();
    list.add(1);
    list.add(2);
    list.add(3);

    printList(list);
}

Unfortunately we will end up getting a compiler error because Java doesn’t know that ArrayIntList is an IntList by default. We have to do something special in the ArrayIntList class to tell Java that it has all the IntList methods.

Making ArrayIntList an IntList

We have all the methods we need implemented in ArrayIntList (and some more!), we just need to tell Java that ArrayIntList is actually an IntList. To do that we just have to change the class header to

public class ArrayIntList implements IntList

This is telling Java that we promise to implement all the methods defined in IntList. If we were to forget a method (say contains(int)), then our class would not compile because it did not implement all the methods IntList asked for!

After adding these two tokens, we are now able to call printList passing in an ArrayIntList. In fact, now that we have an interface for it, it would be better style to define the variable in the client code as

IntList list = new ArrayIntList();

Stacks and Queues

In class today, we will introduce two new abstract data types: Stacks and Queues. Stacks and Queues are, on their own, very simplified data structures that don’t provide many operations. The both support operations to add and remove data from them, but the semantics for the ordering data is removed differs.

We will discuss why these ADTs are useful and how to use their Java counterparts in class, but we just wanted to give a brief overview of the object of study for today’s class.

Stacks

Stacks represent data where each new element gets “stacked on top” of the last.

An useful analogy is to think about a stack of lunch trays. Every time you get a lunch tray, you grab the one from the top. When cafeteria brings a clean lunch tray out to the stack, they put them on top. In other words, you always add new values at the top of the stack and when you remove values, you remove the one most recently added.

We sometimes call Stacks a Last In First Out structure or LIFO for short.

The operations the Stack ADT has are:

  • push: To add a new value at the top of the stack
  • pop: To remove the value from the top of the stack
  • peek: To look at, but not remove, the value at the top of the stack.

Pictorially, this data structure is represented in the following image:

An image of a stack with the numbers 1 (at the bottom), 2, and 3 (at the top)

We’ll discuss more analogies and uses for Stacks in class.

Queues

Queues represent data where each new elements get added at the end and when removing values, we remove from the front.

An useful analogy is to think about a line of people at the store; in fact, some English speaking countries, they refer to this line as a “queue”! Every time a new person comes in line, they join at the back. When the checker is ready to help the next person, they get the person from the front.

We sometimes call Queues a First In, First Out structure or FIFO for short.

The operations the Queue ADT has are:

  • add: To add a new value at the end of the queue
  • remove: To remove the value from the front of the queue
  • peek: To look at, but not remove, the value at the front of the queue

Pictorially, this data structure is represented in the following image:

An image of a queue with the numbers 1 (at the front), 2, and 3 (at the end)

We’ll discuss more analogies and uses for Queues in class.

Recap

  • Interfaces let us define required methods that all classes that implement that interface must have. Importantly, interfaces don’t specify any code for how to do the methods, just a requirement of method headers.
  • A class must explicitly say it implements an interface to be usable in any place the interface type is requested.
  • When using the interface type for your variable, you are restricting yourself to only using methods required by that interface. For example, you couldn’t use extra methods defined in ArrayIntList that aren’t in the IntList interface if your variable is type IntList.
  • Stack is a simple ADT that allow clients to add and remove values only from the top of the stack.
  • Queue is a simple ADT that allows clients to add values at the end of the queue, and remove values from the front.