javaoverridingsealed-classexhaustive

Overriding a method with a sealed class/interface parameter in java


Given this java code:

public class Main {
    public static void main(String[] args) {
        // These are objects of the only 2 classes that implement Parent
        // `sealed` defines that there can't be any other classes that implement Parent
        Child1 child1 = new Child1();
        Child2 child2 = new Child2();
        // I can call myMethod(child1) and myMethod(child2) on myClass
        MyInterface myClass = new MyClass();
        myClass.myMethod(child1);
        myClass.myMethod(child2);
        // I can also call myMethod(child1) and myMethod(child2) on myInterface
        MyInterface myInterface = new MyClass();
        myInterface.myMethod(child1);
        myInterface.myMethod(child2);
    }
}

sealed interface Parent {}
record Child1() implements Parent {}
record Child2() implements Parent {}

interface MyInterface {
    void myMethod(Parent parent);
}

// Why can't I override myMethod(Parent parent) with myMethod(Child1 child1) and myMethod(Child2 child2)?
// This should be exhaustive, since Parent is sealed and there can't be any other classes that implement Parent
class MyClass implements MyInterface {
    //@Override
    public void myMethod(Child1 child1) {
        System.out.println("Child1");
    }

    //@Override
    public void myMethod(Child2 child2) {
        System.out.println("Child2");
    }

    // Why is this method needed after declaring the two methods above?
    @Override
    public void myMethod(Parent parent) {
        if (parent instanceof Child1) {
            myMethod((Child1) parent);
        } else if (parent instanceof Child2) {
            myMethod((Child2) parent);
        }
    }
}

(See also: https://gist.github.com/CC007/768973376ce4ebbfdd3482c5f1bf90b9)

Why can't I override a method with a sealed type (class/interface) as one of the parameters (see MyInterface), by creating methods for the individual children of the sealed type? (see MyClass).

If the Parent interface wasn't sealed, I would understand that this implementation wouldn't guarantee to be exhaustive, but as a sealed type, there can't be any other child classes/records other than Child1 and Child2.

So why can't I omit the myMethod(Parent parent) method in MyClass and why can't I define @Override on the other 2 methods in MyClass?


Solution

  • Why can't I override a method with a sealed type (class/interface) as one of the parameters (see MyInterface), by creating methods for the individual children of the sealed type? (see MyClass).

    This is not the way Java works. According to the specification:

    8.4.8.1. An instance method mC [...] overrides [...] another method mA [...], if all of the following are true: [...] The signature of mC is a subsignature (§8.4.2) [...].

    8.4.2. Two methods [...] have the same signature if they have the same name, [...], and, [...] the same formal parameter types.

    https://docs.oracle.com/javase/specs/jls/se21/html/jls-8.html#jls-8.4.8.1

    One example of how Java sealed types work related to your example is pattern matching. You could write myMethod as follows:

    @Override
    public void myMethod(Parent parent) {
      switch (parent) {
        case Child1 c -> System.out.println("Child1");
        case Child2 c -> System.out.println("Child2");
        //exhaustive because of sealed type
      }
    }