13

I'm trying to write a library of collection interfaces that implement most of the methods in the standard Collection API using the new default method syntax in Java 8. Here's a small sample of what I'm going for:

public interface MyCollection<E> extends Collection<E> {
    @Override default boolean isEmpty() {
        return !iterator().hasNext();
    }
    //provide more default overrides below...
}

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override default Iterator<E>iterator(){
        return listIterator();
    }
    //provide more list-specific default overrides below...
}

However, even this simple example is met with a compiler error:

error: interface MyList<E> inherits abstract and default
       for isEmpty() from types MyCollection and List

From my understanding of default methods, this should be allowed since only one of the extended interfaces provides a default implementation, but apparently that's not the case. What's going on here? Is there a way to get this to do what I want?

Ryan Hilbert
  • 1,805
  • 1
  • 18
  • 31

2 Answers2

11

This is explained in section 9.4.1.3 (Inheriting Methods with Override-Equivalent Signatures) of the Java Language Specification:

It is possible for an interface to inherit several methods with override-equivalent signatures (§8.4.2).

...

Similarly, when an abstract and a default method with matching signatures are inherited, we produce an error. In this case, it would be possible to give priority to one or the other - perhaps we would assume that the default method provides a reasonable implementation for the abstract method, too. But this is risky, since other than the coincidental name and signature, we have no reason to believe that the default method behaves consistently with the abstract method's contract - the default method may not have even existed when the subinterface was originally developed. It is safer in this situation to ask the user to actively assert that the default implementation is appropriate (via an overriding declaration).

So since both MyCollection and List define a method isEmpty() and one is default and the other is abstract, the compiler requires the subinterface to explicitly declare which one it should inherit by overriding the method again. If you want the default method of MyCollection to be inherited, then you can invoke it in the overriding implementation:

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override default boolean isEmpty() {
        return MyCollection.super.isEmpty();
    }

    @Override default Iterator<E> iterator(){
        return listIterator();
    }
    ...
}

If you want MyList to keep the isEmpty() abstract (which I don't think you want), you can do:

public interface MyList<E> extends MyCollection<E>, List<E> {
    @Override boolean isEmpty();

    @Override default Iterator<E> iterator(){
        return listIterator();
    }
    ...
}
Community
  • 1
  • 1
M A
  • 71,713
  • 13
  • 134
  • 174
  • The need to duplicate all these method signatures is disappointing, but this seems like exactly what I was looking for. Accepted, assuming there's no way to avoid this. – Ryan Hilbert Sep 24 '15 at 06:14
  • 2
    The really bad thing about it is that there is no reason for `List` to redeclare `isEmpty()`. It’s sole purpose seems to be to change the documentation comment from the inherited “Returns true if this collection contains no elements.” to the more specific “Returns true if this list contains no elements.” Without this, everything would work as before but the compile error from this question would be gone as `MyCollection.isEmpty()` overrides `Collection.isEmpty()`… – Holger Sep 24 '15 at 10:09
  • 1
    @Holger You're right in this case. But for other methods like `toArray`, the documentation differs more in terms of the method contract. I think this problem comes as a price for using default methods. – M A Sep 24 '15 at 10:36
  • 1
    This also looks similar to the diamond problem of multiple inheritance. – M A Sep 24 '15 at 10:38
  • @Holger This is a fine example where the "obviously right" answer turns out to be wrong. Given simple conflicts like this, it seems "obvious" that abstract-default conflicts should be resolved in favor of the default (and in fact, this is where we started). However, as the hierarchies get more complex, the "obvious" thing progresses from less obvious to confusing to approaching bizarre, and starts to demand more complex tools for conflict resolution, which in turn end up offering a very poor return-on-complexity. Far simpler to let such conflicts be resolved manually -- its not hard. – Brian Goetz Sep 24 '15 at 13:45
  • @Brian Goetz: and even the current rules seem to be [too hard for the compiler to get them right](http://stackoverflow.com/a/32608570/2711488)… – Holger Sep 24 '15 at 13:57
0

change your source code to

public interface MyList<E> extends MyCollection<E>,List<E> {
     @Override 
     default boolean isEmpty(){
           return MyCollection.super.isEmpty();
      }
}

For more information follow the link, default implementation in interface

Community
  • 1
  • 1
Yogi
  • 1,527
  • 15
  • 21