给出以下代码:
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
进行比较,并且只与他进行比较。
我做错了什么?
答案 0 :(得分:13)
问题在Angelika Langer的Generics FAQ #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 :(得分:4)
就你的设计逻辑而言,你没有做错任何事。但是,Java有一个限制,阻止您使用不同的类型参数实现相同的通用接口,这是由于它实现泛型的方式(通过类型擦除)。
在您的代码中,Goalkeeper
继承自Player
Comparable <Player>
的实现,并尝试添加自己的Comparable <Goalkeeper>
;这是不允许的。
解决此限制的最简单方法是覆盖Comparable <Player>
中的Goalkeeper
,将传入的玩家强制转换为Goalkeeper
,并将其与this
守门员进行比较。< / p>
修改强>
public int compareTo (Player otherPlayer) {
Goalkeeper otherGoalkeeper = (Goalkeeper)otherPlayer;
Integer _missedGoals = new Integer(this.missedGoals);
return _missedGoals.compareTo(otherGoalkeeper.getMissedGoals());
}
答案 2 :(得分:0)
我想对现有的好答案补充两点。
如您所知,您的设计无法使用 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>();
现在我们用守门员(仅)填充两个列表并对其进行排序。现在 list1
应该使用 Goalkeeper.compsreTo()
和 list2
进行排序,可能使用 Player.compareTo()
。它开始令人困惑,不是吗?你想要这样的设计吗?您是否更喜欢一种您更明确地了解何时使用哪种比较方法的方法? (是的,我知道,您不能通过变量 list1
和 list2
填充列表。您必须在将它们分配给这两个变量之前填充列表。)
解决方案 1: 使用 compareTo
代替您的 Comparator
方法之一(或两者)。 Comparator<Player>
或 Comparator<Goalkeeper>
或其中之一。例如:
Comparator<Player> playerComparator = Comparator.comparingInt(Player::getScoredGoals);
解决方案 2: 为非守门员的球员引入一个单独的课程。我暂时称它为 FieldPlayer
,因为没有更好的词。 FieldPlayer
和 Goalkeeper
都应该是 Player
的子类。 FieldPlayer
实现了 Comparable<FieldPlayer>
,而 Goalkeeper
已经实现了 Comparable<Goalkeeper>
。现在Player
不需要实现Comparable
,避免了冲突。