5

这是一个显示我的问题的简化示例:

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");
    }
}

SingleTask我提供了方法get和的实现size,它们是AbstractList. 但是,当我编译时MyTask,我仍然会收到如下错误:

MyTask 类型必须实现继承的抽象方法 AbstractCollection.size()

或者

MyTask.java:3:错误:MyTask 不是抽象的,并且不会覆盖 AbstractList 中的抽象方法 get(int)

(取决于编译器)。当然,我使用的是 java 8。

所以我有两个问题:

  1. 为什么我会收到这些错误?我期待它能够识别默认实现。
  2. 如果它不应该那样工作,那么在MyTask不复制整个代码的情况下使用这两种方法的最简单方法是什么?
4

2 回答 2

6

强制SingleTask实现者也实现 的所有方法List并不是很优雅,并且默认方法也不打算用于定义您的SingleTask界面看起来像的 trait-like 实体。

默认方法作为特征是一个坏主意的原因有很多,最明显的一个原因是任何实现者都可以简单地覆盖您的默认方法,从而破坏您的特征。

这正是这里发生的事情:由于AbstractList显式声明get()and size()as abstract,这意味着SingleTask将继承它们,而不是您可能在超级接口中拥有的默认实现。

JLS 8.4.8

类 C 从其直接超类和直接超接口继承所有抽象和默认(第 9.4 节)方法 m ,以下所有情况均成立:

...

  • C 从其直接超类继承的具体方法没有一个签名是 m 的签名的子签名。

记住所有这些,最简单的解决方案可能是这样的:

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();
}

它的缺点是你的任务必须扩展SingleTask,因此不能扩展其他任何东西,从好的方面来说,虽然他们不需要处理任务也是 a List,但他们只需要实现run().

但从长远来看,我更喜欢组合而不是继承,并且任务只是返回一个可运行的列表而不是它们本身就是一个。

于 2016-03-03T11:01:05.793 回答
2
  1. 为什么我会收到这些错误?我期待它能够识别默认实现。

我认为@biziclop 在他的回答中正确地涵盖了这一点。简而言之,作为抽象的AbstractList声明get(int)size()方法,这些优先于您在SingleTask.

  1. 如果它不应该那样工作,那么在MyTask不复制整个代码的情况下使用这两种方法的最简单方法是什么?

最简单的方法是覆盖get(int)size()中的方法MyTask,以便它们委托给SingleTask接口中的默认方法:

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();
    }
}

使用这种方法,您可以SingleTask. 我不认为这是一件坏事(至少,您不需要使用属性)。此外,编写这些方法是有意义的,这样您就可以选择提供默认实现的接口。

于 2016-03-03T13:41:13.337 回答