Почему я не могу реализовать несколько интерфейсов? - PullRequest
6 голосов
/ 12 октября 2019

Я создаю игру, в которой объекты реализуют интерфейсы для анимации. У меня есть родительский интерфейс для анимации. Вот сокращенная версия:

public interface Animates<S extends Animator> {
    S createAnimator(long animationTime);
}

Кроме того, у меня есть несколько интерфейсов, расширяющих этот интерфейс. Два примера:

public interface AnimatesPaint extends Animates<PaintAnimator> {
    PaintAnimator createPaintAnimator(long animationTime);

    default PaintAnimator createAnimator(long animationTime) {
        return createPaintAnimator(animationTime);
    }

}

и

public interface AnimatesPosition extends Animates<PositionAnimator> {
    PositionAnimator createPositionAnimator(long animationTime);

    @Override
    default PositionAnimator createAnimator(long animationTime) {
        return createPositionAnimator(animationTime);
    }

}

Как видите, интерфейсы, расширяющие Animates, переопределяют метод createAnimator, чтобы делегировать логику createAnimatorк классу, который реализует интерфейс.

Причина, по которой я это делаю, заключается в том, что я хочу иметь элемент экрана (который может анимировать), который реализует несколько интерфейсов анимации (например, оба AnimatesPosition для перемещения элемента и AnimatesPaintизменить его цвет).

Однако это, по-видимому, не работает. Когда я реализую оба в одном классе (показано ниже), я получаю ошибку компиляции:

createAnimator (long) в AnimatesPaint конфликтует с createAnimator (long) в AnimatesPosition;попытка использовать несовместимые типы возврата

Вот пример класса, который реализует оба интерфейса Animates:

public class ScreenElement implements AnimatesPaint, AnimatesPosition {
    @Override
    PositionAnimator createPositionAnimator(long animationTime) {
        return new PositionAnimator(animationTime);
    }
    @Override
    PaintAnimator createPaintAnimator(long animationTime) {
        return new PaintAnimator(animationTime);
    }
}

Так что я не понимаю, что оба AnimatesPaintи AnimatesPosition уже реализует createAnimator. Тем не менее, сообщение об ошибке указывает на то, что createAnimator также необходимо реализовать с помощью ScreenElement! Если бы createAnimator еще не было бы реализовано, я бы понял, почему происходит конфликт.

Где моя логика идет не так?

В конце концов, чего я хочу достичь, должен иметь общий метод, который может инициировать любой тип анимации. Например:

public class AnimationStarter<S extends Animates, T> {

    public void startAnimation(S animates, T start, T target, long animationTime) {
        Animator animator = animates.createAnimator(animationTime);
        animator.init(start, target);
        animates.setAnimator(animator);
        animator.startAnimation();
    }
}

- Редактировать -

По запросу, вот объявление Аниматора

public abstract class Animator<T> {}

и один из его расширенных классов

public class PositionAnimator extends Animator<Point>{}

Ответы [ 2 ]

4 голосов
/ 12 октября 2019

Так что я не понимаю, что и AnimatesPaint, и AnimatesPosition уже реализуют createAnimator.

Да, и эти реализации конфликт друг с другом. Если бы вы могли это сделать, тип вашего результирующего класса должен был бы предоставить два createAnimator метода, которые различаются только по типу возвращаемого значения. Java не допускает перегрузок, которые различаются только типом возвращаемого значения, поэтому вы не можете этого сделать. сигнатура метода для целей перегрузки не включает тип возвращаемого значения.

Даже если бы они имели один и тот же тип возвращаемого значения (Animator), вы бы имели две перегрузки сточно такая же подпись, которую вы не можете сделать.

Они должны быть отдельными методами - например, с отдельными сигнатурами, которые можно дифференцировать - если они будут реализованы в одном классе.


В комментарии вы спросили:

Но конфликт не разрешен тем фактом, что метод уже был переопределен AnimatesPaint и AnimatesPosition? Таким образом, реализующий класс ScreenElement не должен реализовывать метод createAnimator, поэтому конфликт не возникнет.

Нет, поскольку сам класс предоставляет эти методы (или, скорее, должен) как часть своей подписи. По сути, предположим, что вы можете создать класс, и у вас есть его экземпляр s. Что бы s.createAnimator(300L) сделал? Какой компилятор выбрать?

Открытый тип класса состоит из всех его открытых членов, включая все открытые члены всех интерфейсов, которые он реализует. Таким образом, на уровне типов невозможно, чтобы два интерфейса реализовали методы с одинаковой сигнатурой.

2 голосов
/ 12 октября 2019

Если вы вызываете метод ScreenElements createAnimator (), какой он должен использовать? Вот на что жалуется компилятор. Вы должны сказать ему, что делать, когда вызывается этот метод. На основании кода я не уверен. Итак, вы правы, что ScreenElement должен реализовать метод create animator, и компилятор знает, что делать при вызове этого метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...