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?

2 Answers 2

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(); } ... } 
Sign up to request clarification or add additional context in comments.

6 Comments

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.
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 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.
This also looks similar to the diamond problem of multiple inheritance.
@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.
|
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

1 Comment

the call to MyCollection.super.isEmpty() is superfluous

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.