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?
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