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 implementsI
, so possible problems come from the addition of these default methods toI
: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, sinceA
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 renamingfoobar
tofoobarOriginal
inA
(as required by the question). -
Class
B
: This class implements bothI
andJ
, so possible problems come from the fact that the following default methods have been added toI
: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
andJ
provide a default method with this signature (remember that interface methods are implicitly public). It is ambiguous which of these methodsB
should inherit, so the compiler is unhappy.The solution is to add an implementation of
foobar
with this signature toB
. As explained in the question, any implementation that respects the method signature is fine; in the sample solution I have provided afoobar
that usesreturn I.super.foobar(x);
to choose the default implementation offoobar
coming fromI
. -
Class
C
: This class implementsK
, which extendsI
andJ
. Problems could therefore come from the default methods added byI
andJ
(described above), and the following default method added byK
:default int foobar(int x) { return I.super.foobar(x); }
The problem we had with class
B
, where it was unclear whichint foobar(int x)
method should be chosen given the default provided byI
andJ
, is not a problem here sinceK
provides the above default method of this signature, which resolves the ambiguity (by choosingI
'sfoobar
).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 anIOException
, while the default zero-argumentfoobar
specified byI
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-argumentfoobar
specified in each ofI
,J
andK
. 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 tofoobarOriginal
.