Interface Extension

Interfaces can extend other interfaces.

interface Dog {
    void bark();
}

record Color(int r, int g, int b) {}

interface ColoredDog extends Dog {
    Color color();
}

This means a few things. First an implementing class must specify all the methods from both.

class Clifford implements ColoredDog {
    @Override
    public void bark() { // Must define the methods on Dog
        IO.println("BARK BARK");
    }

    @Override
    public Color color() { // As well as on ColoredDog
        return new Color(255, 0, 0); // Red
    }
}

Second, interface extension "establishes a subtyping relationship." Something which implements a sub-interface can be used in a place expecting the super-interface.

void main() {
    Clifford clifford = new Clifford();
    clifford.bark();
    IO.println(clifford.color());

    IO.println("-------");
    // clifford is a "ColoredDog"
    ColoredDog coloredDog = clifford;
    // So all the methods on ColoredDog are available
    coloredDog.bark();
    IO.println(coloredDog.color());


    IO.println("-------");
    // all "ColoredDog"s are also "Dog"s
    Dog dog = coloredDog;
    // So you can use the methods from Dog, but not any
    // from ColoredDog
    dog.bark();
    // IO.println(dog.color()); - Won't work

    IO.println("-------");
    // and all "Dog"s are "Object"s
    Object o = dog;
    // So you only have access to the methods from Object unless you
    // use instanceof to recover the actual type of the object
    if (o instanceof ColoredDog c) {
        c.bark();
        IO.println(c.color());
    }
}