不能与父子孙子继承相媲美

时间:2012-01-01 18:59:27

标签: java inheritance comparable

给出以下代码:

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 个答案:

答案 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)

我想对现有的好答案补充两点。

  1. 即使有可能,您也可能不想要自己尝试过的设计,这是有原因的。有点蓬松。
  2. 除了 Sergey Kalinichenko 提出的解决方案之外,还有其他可能的解决方案。

你的设计有缺点

如您所知,您的设计无法使用 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()。它开始令人困惑,不是吗?你想要这样的设计吗?您是否更喜欢一种您更明确地了解何时使用哪种比较方法的方法? (是的,我知道,您不能通过变量 list1list2 填充列表。您必须在将它们分配给这两个变量之前填充列表。)

几个解决方案

解决方案 1: 使用 compareTo 代替您的 Comparator 方法之一(或两者)。 Comparator<Player>Comparator<Goalkeeper> 或其中之一。例如:

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

解决方案 2: 为非守门员的球员引入一个单独的课程。我暂时称它为 FieldPlayer,因为没有更好的词。 FieldPlayerGoalkeeper 都应该是 Player 的子类。 FieldPlayer 实现了 Comparable<FieldPlayer>,而 Goalkeeper 已经实现了 Comparable<Goalkeeper>。现在Player不需要实现Comparable,避免了冲突。