Unit 6: Interfaces

Table of Contents

  1. Interfaces
    1. Interface Inheritance
    2. Fields
    3. Methods
  2. Interfaces vs Abstract Classes
  3. Comparable
    1. Generics
    2. compareTo

Interfaces

Want to get even more generalized? Let’s talk about interfaces. You can think of interfaces as another level of abstraction when it comes to classes. By convention, interface names can be nouns or adjectives, such as Animal or Edible.

To declare an interface, simply use the interface keyword. In general, it looks like this:

modifier(s) interface InterfaceName {
    // interface body
}

Interface Inheritance

A class or abstract class can inherit from an interface. This is called interface inheritance. Interface inheritance (we’ll just call it inheritance from now on) represents an is-kind-of relationship. It’s weaker than regular inheritance. For example, we might have an interface called Edible, which is implemented by Fruit. Apple extends Fruit. An Apple is Edible, but it is more closely related to Fruit.

Interface inheritance also allows us to relate classes that otherwise might be unrelated. For example, we might have a Plant class which is extended by Basil. Basil could implement Edible, even though it’s clearly unrelated to Fruit.

            Edible
            /    \
        Fruit     \   Plant
        /          \  /
    Apple         Basil

To inherit from an interface, you use the implements keyword. It generally looks like this:

modifier(s) class ClassName implements InterfaceName

This is what our Edible example might look like with interface and class bodies omitted.

public interface Edible {
    ...
}
public class Fruit implements Edible {
    ...
}
public class Apple extends Fruit {
    ...
}
public class Plant {
    ...
}
public class Basil extends Plant implements Edible {
    ...
}

Note: A class can only extend 1 superclass, but it can implement multiple interfaces (just separate them with commas).

Note: Interfaces are basically treated like superclasses, so the subclass inherits all of the interface’s fields and methods. Additionally, an instance of a subclass is also treated as an instance of the interface. (For example, an Apple object is an instance of Edible and Fruit.)

Fields

Interfaces can only contain constants. All fields in an interface are public static final, so you can choose to explicitly write that in front of all of the interface’s fields, or omit them.

For example, the two interfaces below are equivalent.

public interface Example {
    public static final MY_CONSTANT = 50;
}
public interface Example {
    MY_CONSTANT = 50;
}

Note that since the constants are static, you access them through the interface name. In the example above, we could access the constant by writing Example.MY_CONSTANT.

Methods

All interface methods are public abstract, so those keywords can also be omitted or explicitly stated when defining an interface.

The two interfaces below are equivalent.

public interface Example {
    public static final MY_CONSTANT = 50;

    public abstract void someMethod();
}
public interface Example {
    MY_CONSTANT = 50;

    void someMethod();
}

Interfaces vs Abstract Classes

Remember that while both interfaces and abstract classes are more “generalized” forms of classes, they are not identical! Here is a table that summarizes differences between the two.

Interfaces Abstract Classes
All methods are public abstract Methods can be concrete or abstract
All fields are public static final Fields don’t need to be constants
Inherit using implements Inherit using extends
Can inherit multiple interfaces Can only inherit 1 class
Weak inheritance Somewhat stronger inheritance
No constructors Can have constructor(s), but can’t make instance using new
Name can be noun or adjective Name is a noun

Comparable

There are many interfaces built-in to Java. One that is useful is the Comparable interface, which contains the abstract compareTo method. This allows a developer to define what it means for an object to be greater than, less than, or equal to another object.

Generics

Before we talk about Comparable, we need to do a very brief and simplified introduction to generics in Java. Basically, generics allow you to turn classes into parameters for things like methods or interfaces. You can tell that a structure uses generics if it has the < and > symbols. For example:

public interface Comparable<T>

The T is simply a placeholder for a class type (it doesn’t work with primitives). For example, if you want to implement Comparable to compare two Point objects, you would replace T with Point:

public class Point implements Comparable<Point> {
    ...
}

compareTo

Since Comparable contains the abstract compareTo method, we need to implement it in our concrete Point class.

The compareTo method is defined as follows in the Comparable interface:

public interface Comparable<T> {
    public int compareTo(T o);
}

Notice that the type is listed as T. That’s because we’re generalizing the compareTo method to work for any class. T could be replaced with Point, Circle, Apple, or any other class.

Another thing to notice is that the return type is int. This is because a positive integer represents “greater than”, 0 represents “equal to”, and a negative integer represents “less than.”

Analyze the code below, which implements the compareTo method in the Point class such that the two objects’ x coordinates are checked, and then y coordinates. For example, (5, 5) is considered “greater than” (4, 5).

Point

public class Point implements Comparable<Point> {
    public double x;
    public double y;

    public Point() {
        x = 0;
        y = 0;
    }

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public int compareTo(Point pt) {
        // compare on x coordinate first
        if (x > pt.x) {
            return 1;
        } else if (x == pt.x) {
            // if x coordinates are equal, compare y
            if (y > pt.y) {
                return 1;
            } else if (y == pt.y) {
                return 0;
            } else {
                return -1;
            }
        } else {
            return -1;
        }
    }
}

Now let’s actually use the compareTo method in a driver class, TestPoint. Analyze the code and output below.

TestPoint

public class TestPoint {
    public static void main(String[] args) {
        Point[] points = {
            new Point(5, 5),
            new Point(4, 5),
            new Point(5, 5),
            new Point(6, 5),
            new Point(5, 6),
        };

        for (int i = 1; i < points.length; i++) {
            int comparisonResult = points[0].compareTo(points[i]);
            if (comparisonResult > 0) {
                System.out.println("Greater than");
            } else if (comparisonResult == 0) {
                System.out.println("Equal to");
            } else {
                System.out.println("Less than");
            }
        }
    }
}

Output

Greater than
Equal to
Less than
Less than

Note: The compareTo method is often used to determine the natural ordering of elements. That is why the Arrays.sort(arr) method sorts elements in an array using the compareTo method. If you’d like to see a demonstration, see SortPoints.java.

Note: When implementing the compareTo and equals methods, you should make sure that compareTo returns 0 if and only if equals returns true to be consistent.

Note: For more information on the Comparable interface, you can read its documentation here.