Java Arena
Step 5 of 9

Lists of an interface, and interfaces as return types

A phone's media player plays voice memos and radio shows. From every item in its queue, it needs a title, a length in seconds, and a way to play it:

public interface Playable {
    String title();
    int seconds();
    void play();
}

A list of Playables

A list's element type can be an interface too. Then the list holds objects of any class that implements it:

Main.java
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Playable> queue = new ArrayList<>();
        queue.add(new VoiceMemo("Groceries", 45));
        queue.add(new RadioShow("Morning News", 5));
        queue.add(new VoiceMemo("Door code", 12));

        int total = 0;
        for (Playable item : queue) {
            item.play();
            total += item.seconds();
        }
        System.out.println("In the queue: " + total + " seconds");

        Playable longest = longest(queue);
        System.out.println("Longest: " + longest.title());
    }

    public static Playable longest(ArrayList<Playable> queue) {
        Playable longest = queue.get(0);
        for (Playable item : queue) {
            if (item.seconds() > longest.seconds()) {
                longest = item;
            }
        }
        return longest;
    }
}
Playable.java
public interface Playable {
    String title();
    int seconds();
    void play();
}
VoiceMemo.java
public class VoiceMemo implements Playable {
    private String title;
    private int seconds;

    public VoiceMemo(String title, int seconds) {
        this.title = title;
        this.seconds = seconds;
    }

    @Override
    public String title() {
        return this.title;
    }

    @Override
    public int seconds() {
        return this.seconds;
    }

    @Override
    public void play() {
        System.out.println("Memo: " + this.title);
    }
}
RadioShow.java
public class RadioShow implements Playable {
    private String name;
    private int minutes;

    public RadioShow(String name, int minutes) {
        this.name = name;
        this.minutes = minutes;
    }

    @Override
    public String title() {
        return this.name;
    }

    @Override
    public int seconds() {
        return 60 * this.minutes;
    }

    @Override
    public void play() {
        System.out.println("On air: " + this.name);
    }
}
Output
Memo: Groceries
On air: Morning News
Memo: Door code
In the queue: 357 seconds
Longest: Morning News
  • ArrayList<Playable> holds memos and shows side by side. add accepts a VoiceMemo because a VoiceMemo is a Playable.
  • The loop variable item has the type Playable. Each time round, it refers to one object, and that object's play runs: a memo's prints Memo:, a show's prints On air:. The loop doesn't know or care which kind each item is.
  • A new kind of item, such as a chapter of an audiobook, would be one more class that implements Playable. The loop wouldn't change.
  • The class names VoiceMemo and RadioShow appear only where the objects are made. Everything after that works with Playable.

Returning a Playable

longest has the return type Playable. It returns whichever object in the queue has the most seconds, and the caller keeps it in a Playable variable, so the caller may use Playable's methods on it.

  • The object that comes back is still what it was: the radio show. longest(queue).play() would print On air: Morning News.
  • A method can also create an object and return it as a Playable. The caller then doesn't even need to know which class it got. The last challenge of this step does that.

Your task

Challenge 1 of 3

Write the method find(ArrayList<Playable> queue, String title), which returns the item in the queue with that title, or null if there's none. If several items have the title, return the first of them. Playable.java, VoiceMemo.java and RadioShow.java are ready. The check runs:

ArrayList<Playable> queue = new ArrayList<>();
queue.add(new VoiceMemo("Groceries", 45));
queue.add(new RadioShow("Morning News", 5));
Playable found = find(queue, "Morning News");
found.play();
System.out.println(found.seconds());
System.out.println(find(queue, "Weather") == null);

and expects:

On air: Morning News
300
true

Challenge 2

The player plays only the long items in the queue: those of at least two minutes. What does this program print?

Challenge 3

The player reads its queue from lines of text such as memo,Groceries,45 or show,Morning News,5: the kind, the title, and the length, in seconds for a memo and in minutes for a show. Write the method fromLine(String line), which makes a new VoiceMemo or a new RadioShow from the line and returns it. Its return type is Playable, so the caller doesn't need to know which class it gets. The kind is always memo or show. The check runs:

Playable first = fromLine("memo,Groceries,45");
Playable second = fromLine("show,Morning News,5");
first.play();
second.play();
System.out.println(first.seconds() + second.seconds());

and expects:

Memo: Groceries
On air: Morning News
345