Невозможно использовать сравнимое с наследием отца-сына-внука
Учитывая следующий код:
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());
}
// ...
}
Я хотел бы добавить два момента к имеющимся хорошим ответам.
- Есть причины, по которым вы можете не захотеть попробовать тот дизайн, который вы пробовали, даже если бы это было возможно. Он немного пушистый.
- Есть и другие возможные решения помимо того, что предлагает Сергей Калиниченко.
У вашего дизайна есть недостатки
Как вы знаете, ваш дизайн невозможен с помощью 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
, и конфликт избегается.