Interfaces

CSCI 1933 – Introduction to Algorithms and Data Structures
Adriana Picoral

Interfaces

  • Non-instantiable (cannot use new)
  • Contain method signatures (and nothing else)
  • Use implements keyword to implement
  • A class may implement multiple interfaces
  • Each interface signature must be implemented
  • Interfaces specify what, not how

Interfaces

When a class implements an interface, it has an “is-a” relationship with the interface

Why Have Interfaces?

  • Code development and abstraction: Define “what” before the “how” is implemented
  • Faster design
  • Program maintenance: Easier maintenance and resilience to change
  • Flexibility
  • ensure compatibility across changes

Practice

You are building a simple notification system.

Different notification types all need to be sent and described, but the details of how they work differ.

We will use an interface to define the shared contract.

Part 1 — Define the Interface

Create an interface called Notification with the following method signatures:

  • String getRecipient() — returns who the notification is going to
  • String getMessage() — returns the body of the notification
  • void send() — prints a formatted line simulating sending the notification

Interface methods are implicitly public because they are a public contract for external use

Part 1 — Solution

public interface Notification {
    String getRecipient();
    String getMessage();
    void send();
}

Part 2 — Implement the Interface

Class EmailNotification

  • Fields: recipientEmail (String), subject (String), body (String)
  • Constructor takes all three fields
  • getRecipient() returns the email address
  • getMessage() returns the subject and body combined: “Subject: … | Body: …”
  • send() prints something like: [EMAIL] To: alice@example.com | Subject: Meeting at 3pm

Part 2 — Solution

public class EmailNotification implements Notification {
    private String recipientEmail;
    private String subject;
    private String body;

    public EmailNotification(String email, String subject, String body) {
        this.recipientEmail = email;
        this.subject = subject;
        this.body = body;
    }

    @Override
    public String getRecipient() {
        return recipientEmail;
    }

    @Override
    public String getMessage() {
        return "Subject: " + subject + " | Body: " + body ;
    }

    @Override
    public void send() {
        System.out.print("[EMAIL] To: " + recipientEmail);
        System.out.println(" | Subject: " + subject);
    }
}

Part 2 — Implement the Interface

Class TextNotification

  • Fields: phoneNumber (String), message (String)
  • Constructor takes both fields
  • If message is longer than 160 characters, the constructor should truncate it to 160 and print a warning
  • getRecipient() returns the phone number
  • getMessage() returns the message text
  • send() prints something like: [TEXT] To: 555-1234 | Message: Don’t forget the meeting!

Part 2 — Solution

public class TextNotification implements Notification {
    private String phoneNumber;
    private String message;

    public TextNotification(String phoneNumber, String message) {
        this.phoneNumber = phoneNumber;
        if (message.length() > 160) {
            System.out.println("Message longer than 160, truncated");
            message = message.substring(0, 159);
        }
        this.message = message;
    }

    @Override
    public String getRecipient() {
        return phoneNumber;
    }

    @Override
    public String getMessage() {
        return message;
    }

    @Override
    public void send() {
        System.out.print("[TEXT] To: " + phoneNumber);
        System.out.println(" | Message: " + message);
    }
}

Part 3 — Use the Interface

In a main method, create the following array:

Notification[] inbox = {
      new EmailNotification("alice@example.com", "Meeting at 3pm", "Please join the Zoom link."),
      new TextNotification("555-1234", "Don't forget the meeting!"),
      new EmailNotification("bob@example.com", "Homework due", "Project 2 is due Friday."),
      new TextNotification("555-5678", "Your package has been delivered.")
  };

Loop over the array and call send() on each notification, then print a count of how many notifications were sent.

Part 3 — solution

for (Notification n : inbox) n.send();

Part 4 — Add a Filter

Write a static method (outside of main) with this signature:

public static void sendToRecipient(Notification[] notifications, String recipient)

It should call send() only on notifications whose getRecipient() matches the given recipient. Call it from main to send only Alice’s notifications.

Part 4 — Solution

public static void sendToRecipient(Notification[] notifications, String recipient) {
        for (Notification n : notifications) {
            if (n.getRecipient().toLowerCase().contains(recipient.toLowerCase())) n.send();
        }
    }

Submit solution to gradescope

Submit your solution files to gradescope:

  • Notification.java
  • TextNotification.java
  • EmailNotification.java

Discussion Questions

  1. In Part 3, the array is typed as Notification[]. What would you lose if you had used two separate arrays — EmailNotification[] and TextNotification[] — instead?
  2. The method in Part 4 takes a Notification[] parameter. Could you pass it an array that contains a mix of EmailNotification and TextNotification objects? Why?
  3. If you added a PushNotification class later, what would it need to do to work with the Part 4 method without any changes to that method?

The Comparable interface

Numbers and Strings are easy to compare.

  • Is 100 smaller, equal, or larger than 50?
  • Is apple smaller, equal, or larger than kiwi?

What if we have notifications or chairs? How to we compare chairs?

The Comparable interface

  • public interface Comparable<T>
  • contains only a single signature which the programmer must implement: int compareTo(T o)
    • x.compareTo(y) should return 0 if x.equals(y) is true
    • otherwise, it returns a negative value if this object is less than o, or a positive value if this object is greater than 0.

Implementing the Comparable interface

  • Comparable interface supports generic types
  • Generics verify that compareTo() is applied only to data of a compatible type
  • Mismatched types to compareTo() are detected at compile time

Check how the String class implements Comparable

Practice

Create a Chair class that implements Comparable<T>

import java.util.Arrays;

Chair[] chairs = new Chair[]{
                       new Chair(10, 15),
                       new Chair(4, 10),
                       new Chair(10, 5),
                       new Chair(1, 5)
                     };

Arrays.sort(chairs); // sorting based on the compareTo method

Submit your Chair.java file to gradescope

Solution

public class Chair implements Comparable<Chair> {
    private int width;
    private int depth;

    public Chair(int width, int depth) {
        this.width = width;
        this.depth = depth;
    }

    public int getDepth() {
        return depth;
    }

    public int getWidth() {
        return width;
    }

    public int getArea() {
        return width * depth;
    }

    @Override
    public int compareTo(Chair o) {
        if (o.getArea() == this.getArea()) return 0;
        if (o.getArea() < this.getArea()) return 1;
        return -1;
    }

    @Override
    public String toString() {
        return width + " " + depth;
    }
}

Abstract Class

How would we implement compareTo(Notification other) in our Notification project so that our Notification[] array would still work?

  • Problem: We cannot use the implements in an interface
  • Solution: abstract class

Looking Back

  • Challenge: How is implementing an interface different from overriding a classes’ methods?
  • For example, if Object (which contains toString() and equals()) was an interface rather than a class, how would things change?