Невозможно использовать сравнимое с наследием отца-сына-внука

Учитывая следующий код:

public abstract class Participant {
    private String fullName;

    public Participant(String newFullName) {
        this.fullName = new String(newFullName);
    }

    // some more code 
}


public class Player extends Participant implements Comparable <Player> {    
    private int scoredGoals;

    public Player(String newFullName, int scored) {
        super(newFullName);
        this.scoredGoals = scored;
    }

    public int compareTo (Player otherPlayer) {
        Integer _scoredGoals = new Integer(this.scoredGoals);
        return _scoredGoals.compareTo(otherPlayer.getPlayerGoals());
    }

    // more irrelevant code 
}

public class Goalkeeper extends Player implements Comparable <Goalkeeper> { 
    private int missedGoals;        

    public Goalkeeper(String newFullName) {
        super(newFullName,0);
        missedGoals = 0;
    }

    public int compareTo (Goalkeeper otherGoalkeeper) {
        Integer _missedGoals = new Integer(this.missedGoals);
        return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
    }

    // more code 
}

Проблема в том, что Goalkeeper не выполнит

Когда я пытаюсь скомпилировать этот код, Eclipse выдает:

The interface Comparable cannot be implemented more than once with 
different arguments: Comparable<Player> and Comparable<Goalkeeper>

Я не пытаюсь сравнивать с Player, но с Goalkeeperи только с ним.

Что я делаю неправильно?

3 ответа

Решение

Что касается логики вашего дизайна, вы не делаете ничего плохого. Однако у Java есть ограничение, которое не позволяет вам реализовать один и тот же универсальный интерфейс с различными параметрами типа, что связано с тем, как он реализует универсальные интерфейсы (посредством стирания типов).

В вашем коде Goalkeeper наследуется от Player его реализация Comparable <Player>и пытается добавить Comparable <Goalkeeper> своего собственного; это не разрешено

Самый простой способ устранить это ограничение - переопределить Comparable <Player> в Goalkeeperбросить игрока, переданного в Goalkeeperи сравнить его с this вратарь.

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

public int compareTo (Player otherPlayer) {
    Goalkeeper otherGoalkeeper = (Goalkeeper)otherPlayer;
    Integer _missedGoals = new Integer(this.missedGoals);
    return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
}

Эта проблема описана в разделе "Часто задаваемые вопросы об Анжелике Лангер" № 401:

Может ли класс реализовать разные экземпляры одного и того же универсального интерфейса?

Нет, тип не должен прямо или косвенно наследоваться от двух разных экземпляров одного и того же универсального интерфейса.

Причиной такого ограничения является перевод по типу стирания. После стирания типа различные экземпляры одного и того же универсального интерфейса преобразуются в один и тот же необработанный тип. Во время выполнения больше нет различий между различными экземплярами.

(Я настоятельно рекомендую ознакомиться с полным описанием проблемы: это более интересно, чем то, что я цитировал.)

Чтобы обойти это ограничение, вы можете попробовать следующее:

public class Player<E extends Player> extends Participant implements Comparable<E> {
    // ...
    public int compareTo(E otherPlayer) {
        Integer _scoredGoals = this.scoredGoals;
        return _scoredGoals.compareTo(otherPlayer.getPlayerGoals());
    }
    // ...
}


public class Goalkeeper extends Player<Goalkeeper> {
    // ...
    @Override
    public int compareTo(Goalkeeper otherGoalkeeper) {
        Integer _missedGoals = this.missedGoals;
        return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
    }
    // ...
}

Я хотел бы добавить два момента к имеющимся хорошим ответам.

  1. Есть причины, по которым вы можете не захотеть попробовать тот дизайн, который вы пробовали, даже если бы это было возможно. Он немного пушистый.
  2. Есть и другие возможные решения помимо того, что предлагает Сергей Калиниченко.

У вашего дизайна есть недостатки

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

Предположим, что с вашим дизайном у нас есть:

          Goalkeeper goalkeeper1 = new Goalkeeper("Imene");
    Goalkeeper goalkeeper2 = new Goalkeeper("Sofia");
    Player goalkeeper3 = new Goalkeeper("Maryam");

    goalkeeper1.compareTo(goalkeeper2); // would call Goalkeeper.compareTo(Goalkeeper)
    goalkeeper1.compareTo(goalkeeper3); // would call Player.compareTo(Player)

Мы можем пойти еще дальше:

          List<? extends Player> list1 = new ArrayList<Goalkeeper>();
    List<? extends Player> list2 = new ArrayList<Player>();

Теперь заполняем оба списка вратарем (только) и сортируем их. Теперь следует отсортировать с помощью Goalkeeper.compsreTo() и, вероятно, используя Player.compareTo(). Это начинает сбивать с толку, не так ли? Хотели бы вы такой дизайн? Вы бы предпочли тот, в котором вы более четко указываете, какой способ сравнения и когда используется? (Да, я знаю, нельзя заполнять списки через переменные list1 а также list2. Вам нужно будет заполнить списки, прежде чем назначать их этим двум переменным.)

Пара решений

Решение 1. Вместо одного из ваших compareTo методы (или оба из них) используют Comparator. Либо Comparator<Player> или Comparator<Goalkeeper>или по одному каждого. Например:

          Comparator<Player> playerComparator = Comparator.comparingInt(Player::getScoredGoals);

Решение 2. Ввести отдельный класс для игроков, не являющихся вратарем. Я звоню ему сейчас из-за отсутствия лучшего слова. Оба и должны быть подклассами. FieldPlayer орудия Comparable<FieldPlayer> а также Goalkeeper уже реализует Comparable<Goalkeeper>. Сейчас Player не нужно реализовывать Comparable, и конфликт избегается.

Другие вопросы по тегам