Skip to content

Latest commit

 

History

History
executable file
·
77 lines (50 loc) · 4.02 KB

17b1.md

File metadata and controls

executable file
·
77 lines (50 loc) · 4.02 KB

Back to questions

Solution to 17b1: Default methods

A possible solution is provided via the classes and interfaces under solutions/code/tutorialquestions/question17b1/afterdefault.

  • Class A: This class implements I, so possible problems come from the addition of these default methods to I:

    default int foobar() {
      return bar(foo());
    }
    
    default int foobar(int x) {
      return bar(foo(x));
    }
    

    The zero-argument version of foobar is not a problem, since A already contains a method with exactly this signature. This overrides the default implementation, and the compiler is happy.

    However, A already contained a method with the following signature:

    public void foobar(int x);

    This is a problem because it has the same argument type as the one-argument default method that has been added to I, but a different return type. Overloading on return types is not allowed in Java, so the compiler is not happy. In the sample solution this is dealt with by renaming foobar to foobarOriginal in A (as required by the question).

  • Class B: This class implements both I and J, so possible problems come from the fact that the following default methods have been added to I:

    default int foobar() {
      return bar(foo());
    }
    
    default int foobar(int x) {
      return bar(foo(x));
    }
    

    and the following to J:

    default int foobar(int x) {
      return bar(foo(x));
    }
    

    The problem is that B does not provide a method with signature:

    public int foobar(int x);

    and both I and J provide a default method with this signature (remember that interface methods are implicitly public). It is ambiguous which of these methods B should inherit, so the compiler is unhappy.

    The solution is to add an implementation of foobar with this signature to B. As explained in the question, any implementation that respects the method signature is fine; in the sample solution I have provided a foobar that uses return I.super.foobar(x); to choose the default implementation of foobar coming from I.

  • Class C: This class implements K, which extends I and J. Problems could therefore come from the default methods added by I and J (described above), and the following default method added by K:

    default int foobar(int x) {
      return I.super.foobar(x);
    }
    

    The problem we had with class B, where it was unclear which int foobar(int x) method should be chosen given the default provided by I and J, is not a problem here since K provides the above default method of this signature, which resolves the ambiguity (by choosing I's foobar).

    However, there are two problematic methods in C, with these signatures:

    public int foobar() throws IOException;
    protected int foobar(int x);
    

    The zero-argument foobar is problematic because it declares that it can throw an IOException, while the default zero-argument foobar specified by I does not claim to throw any exceptions. This is illegal: an implementing class cannot implement an interface method in a manner that throws more exceptions than the original. This would be bad because client code written against the interface would not be prepared for these additional exceptions. (Throwing fewer exceptions, in contrast, is fine.)

    The one-argument foobar is problematic because it has more restricted visibility than the one-argument foobar specified in each of I, J and K. An implementing class is not allowed to decrease the visibility of an interface method that it implements, and here we have an attempt to decrease visibility from public (recall that interface methods are always public) to protected.

    As required by the question, the solution is to rename each of these troublesome foobar methods to foobarOriginal.