I have 4 Classes that look like this:
public interface Foo<T> { ... default boolean isEmpty() { return false; //dummy value real implementation is not relevant for the problem } } public interface CharSequence { //java.lang.CharSequence ... default boolean isEmpty() { return true; //dummy value real implementation is not relevant for the problem } public abstract class Bar<T> implements Foo<T> { ... } public final BarImpl extends Bar<Character> implements CharSequence { //typical diamond problem ... @Override public boolean isEmpty() { //needed to resolve diamond problem return Foo.super.isEmpty() // Compile Error: No enclosing instance of the type Foo<T> is accessible in scope return Bar.super.isEmpty() // Compile Error: No enclosing instance of the type Bar<T> is accessible in scope return CharSequence.super.isEmpty() // compiles }
Why can’t I access the default implementation coming from extending Bar?
Advertisement
Answer
BarImpl
can not invoke Foo
’s default
method explicitly, as BarImpl
is not directly implementing Foo
. It’s extending Bar
which directly implements Foo
, hence, it’s Bar
’s decision to override Foo
’s default
method or not.
BarImpl
can only invoke Bar
’s isEmpty()
method via super.isEmpty()
, which may end up at Foo
’s default
method if Bar
decides not to override it or at a concrete method of Bar
if it does override it.
Note that T.super.method()
can only be used if either, T
is a directly implemented super interface (i.e. not already implemented by a super class or another super interface) or if T
is an enclosing type of an inner class. The second use case is the reason for the “No enclosing instance of … is accessible in scope” error message.
import java.util.Objects; class Test { public static void main(String... arg) { System.out.println(new BarImpl().isEmpty()); } } public interface Foo<T> { default boolean isEmpty() { System.out.println(" Foo's default method"); return false; } } public abstract class Bar<T> implements Foo<T> { } public final class BarImpl extends Bar<Character> implements CharSequence { @Override public boolean isEmpty() { System.out.println("calling (Bar) super.isEmpty();"); super.isEmpty(); System.out.println("calling CharSequence.super.isEmpty();"); return CharSequence.super.isEmpty(); } public char charAt(int index) { Objects.checkIndex(index, length()); return (char)('A' + index); } public int length() { System.out.println(" length() [CharSequence's default method]"); return 26; } public CharSequence subSequence(int start, int end) { Objects.checkFromToIndex(start, end, length()); return new StringBuilder(end - start).append(this, start, end); } public String toString() { return new StringBuilder(length()).append(this, 0, length()).toString(); } }
calling (Bar) super.isEmpty(); Foo's default method calling CharSequence.super.isEmpty(); length() [CharSequence's default method] false