Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Must implement default interface method?

Here is a simplified example showing my problem:

import java.util.List;

public interface SingleTask extends List<Runnable>, Runnable {
    default Runnable get(final int x) {
        if (x != 0) {
            throw new IndexOutOfBoundsException();
        }
        return this;
    }

    default int size() {
        return 1;
    }
}

import java.util.AbstractList;

public class MyTask extends AbstractList<Runnable> implements SingleTask {
    @Override
    public void run() {
        System.out.println("hello");
    }
}

In SingleTask I provide implementations for the methods get and size, which are the only abstract methods from AbstractList. However, when I compile MyTask, I still get errors like:

The type MyTask must implement the inherited abstract method AbstractCollection.size()

or

MyTask.java:3: error: MyTask is not abstract and does not override abstract method get(int) in AbstractList

(depending on the compiler). I am, of course, using java 8.

So I have two questions:

  1. Why am I getting these errors? I was expecting it to recognize the default implementations.
  2. If it's not supposed to work like that, then what's the simplest way to use those two methods in MyTask without copying the whole code?
like image 272
aditsu quit because SE is EVIL Avatar asked Mar 03 '16 10:03

aditsu quit because SE is EVIL


People also ask

Do we need to implement default method of interface?

Default interface methods are an efficient way to deal with this issue. They allow us to add new methods to an interface that are automatically available in the implementations. Therefore, we don't need to modify the implementing classes.

Do we need to implement all the methods of interface in SAP?

A class must implement all methods of the interface in its implementation part, with the following exceptions: Interface methods declared as optional using the addition DEFAULT.

CAN interfaces have default implementations?

Members with bodies permit the interface to provide a "default" implementation for the method in classes and structs that do not provide their own implementation. Interfaces may not contain instance state.

What is default implementation of interface in Java?

The Java Tutorials Documentation defines default interface methods: Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces. and then proceeds to supply a convoluted, 50+ line example.


2 Answers

Forcing SingleTask implementors to also implement all the methods of List isn't very elegant, and default methods aren't meant to be used to define trait-like entities, which your SingleTask interface looks like.

There are several reasons why default methods-as-traits is a bad idea, the most obvious one being that any implementor can simply override your default method, ruining your trait.

And this is exactly what is happening here: since AbstractList explicitly declares get() and size() as abstract, it means SingleTask will inherit them, rather than the default implementations you may have had in a superinterface.

JLS 8.4.8:

A class C inherits from its direct superclass and direct superinterfaces all abstract and default (§9.4) methods m for which all of the following are true:

...

  • No concrete method inherited by C from its direct superclass has a signature that is a subsignature of the signature of m.

Bearing all that in mind the simplest solution is probably this:

public abstract class SingleTask extends AbstractList<Runnable> implements Runnable {
    @Override
    public final Runnable get(final int x) {
        if (x != 0) {
            throw new IndexOutOfBoundsException();
        }
        return this;
    }

    @Override
    public final int size() {
        return 1;
    }

    @Override
    public abstract void run();
}

Its drawback is that your tasks must extend SingleTask and thus can't extend anything else, on the plus side though they don't need to deal with the task also being a List, they only need to implement run().

In the long run though, I would prefer composition over inheritance, and tasks simply returning a list of runnables rather than themselves being one.

like image 96
biziclop Avatar answered Oct 05 '22 10:10

biziclop


  1. Why am I getting these errors? I was expecting it to recognize the default implementations.

I think @biziclop correctly covered that in his answer. In short, as AbstractList declares get(int) and size() methods as abstract, these take precedence over your default implementations in SingleTask.

  1. If it's not supposed to work like that, then what's the simplest way to use those two methods in MyTask without copying the whole code?

The easiest would be to override get(int) and size() methods in MyTask, so that they delegate to your default methods in SingleTask interface:

public class MyTask extends AbstractList<Runnable> implements SingleTask {

    @Override
    public void run() {
        System.out.println("hello");
    }

    @Override
    public Runnable get(int index) {
        return SingleTask.super.get(index);
    }

    @Override
    public int size() {
        return SingleTask.super.size();
    }
}

With this approach, you would be kind of delegating to your default methods in SingleTask. I don't think this is a bad thing (at least, you don't need to use an attribute). Besides, it makes sense to write these methods, so that you can choose what interface provides the default implementations.

like image 38
fps Avatar answered Oct 05 '22 10:10

fps