我有两家公司asoft和bsoft的代码。我也不能改变。这是我的情况的简化版本,我敢肯定,它有足够的信息来查找导致问题的原因。

bsoft提供IGang,它代表可以与其他帮派作战的帮派。

package bsoft;

public interface IGang {
    /** @return negative, 0, or positive, respectively
     *          if this gang is weaker than, equal to, or stronger
     *          than the other
     */
    public int compareTo(IGang g);
    public int getStrength();
    public String getName();
    public void attack(IGang g);
    public void weaken(int amount);
}


asoft提供了GangWar,允许IGang进行战斗:

package asoft;
import java.util.*;
import bsoft.*;
/** An `IGang` ordered by identity (name) */
public interface ComparableGang extends IGang, Comparable<IGang> {}

package asoft;
import java.util.*;

public class GangWar {
    public final Set<ComparableGang> gangs = new TreeSet<ComparableGang>();
    public void add(ComparableGang g) {gangs.add(g);}
    public void doBattle() {
        while (gangs.size() > 1) {
          Iterator<ComparableGang> i = gangs.iterator();
          ComparableGang g1 = i.next();
          ComparableGang g2 = i.next();
          System.out.println(g1.getName() + " attacks " + g2.getName());
          g1.attack(g2);
          if (g2.getStrength() == 0) {
              System.out.println(g1.getName() + " smokes " + g2.getName());
              gangs.remove(g2);
          }
          if (g1.getStrength() == 0) {
              System.out.println(g2.getName() + " repels " + g1.getName());
              gangs.remove(g1);
          }
        }
        for (ComparableGang g : gangs) {
            System.out.println(g.getName() + " now controls the turf!");
        }
    }
}


它需要附加的约束,即您提供给它的GangComparable,大概是它可以按名称排序或避免重复。每个帮派(为简便起见,在此使用顺序为Set顺序)都会攻击另一个帮派,直到只剩下一个帮派(如果最后两个有平局,则不包括帮派)。我已经编写了ComparableGang的简单实现来对其进行测试:

import asoft.*;
import bsoft.*;
import java.util.*;

class Gang implements ComparableGang {
    final String name;
    int strength;

    public Gang(String name, int strength) {
        this.name = name;
        this.strength = strength;
    }

    public String getName() {return name;}
    public int getStrength() {return strength;}

    public int compareTo(IGang g) {
        return strength - g.getStrength();
    }

    public void weaken(int amount) {
        if (strength < amount) strength = 0;
        else strength -= amount;
    }

    public void attack(IGang g) {
        int tmp = strength;
        weaken(g.getStrength());
        g.weaken(tmp);

    }

    public boolean equals(Object o) {
      if (!(o instanceof IGang)) return false;
      return name.equals(((IGang)o).getName());
    }
}

class Main {
   public static void main(String[] args) {
       GangWar gw = new GangWar();
       gw.add(new Gang("ballas", 2));
       gw.add(new Gang("grove street", 9));
       gw.add(new Gang("los santos", 8));
       gw.add(new Gang("triads", 9));
       gw.doBattle();
   }
}


测试出来...

$ java Main
ballas attacks los santos
los santos repels ballas
los santos attacks grove street
grove street repels los santos
grove street now controls the turf!


问题是,三合会不会出现在战斗中。实际上,直接在gangs.size()开头打印doBattle()会返回3而不是4。为什么?如何解决?

最佳答案

问题是,三合会不会出现在战斗中。实际上,在doBattle()开始时立即打印gangs.size()会返回3而不是4。为什么?


triadsgrove street的强度均为9。因此,它们在Gang.compareTo方面相等(实现Comparable)。因此,TreeSet中仅允许一个。

如果您不想删除按排序顺序重复的项目,请不要使用TreeSet ...

编辑:ComparableGang接口说明指出了所期望的:

/** An `IGang` ordered by identity (name) */
public interface ComparableGang extends IGang, Comparable<IGang> {}


您的compareTo方法不按“标识(名称)”排序-而是按强度排序。老实说,这首先是一个非常愚蠢的接口,因为asoft创建一个public class GangNameComparator : Comparator<IGang>类非常容易,然后如果他们想订购它,然后将其作为树集的比较器提供按名字。

但是,由于他们建议您实施比较,因此您需要按照界面说明进行操作:

public int compareTo(IGang g) {
    return name.compareTo(g.getName());
}


但是,正如您在评论中(以及Rob的回答中所指出的那样),这与习惯命名为IGang的描述相矛盾:

public interface IGang {
    /** @return negative, 0, or positive, respectively
     *          if this gang is weaker than, equal to, or stronger
     *          than the other
     */
    public int compareTo(IGang g);
}


实现ComparableGang以满足其自己的文档和IGang文档是不可能的。从asoft的角度来看,这基本上是设计破坏的。

任何代码都应该能够使用IGang实现,仅了解IGang,并依赖于IGang合同之后的实现。但是,asoft通过在扩展IGang的接口中要求不同的行为打破了这一假设。

只要他们没有违反ComparableGang的现有要求,他们在IGang中添加更多要求是合理的。

请注意,这是C#和Java之间的重要区别。在C#中,两个具有相同签名的不同接口中的两个函数可以组合为一个继承了它们和the two methods remain distinct and accessible的接口。在Java中,这两种方法都是完全抽象的,并且具有相同的签名,因此它们是considered to be the same method,并且实现组合接口的类只有一个这样的方法。因此,在Java中,ComparableGang无效,因为它不能具有满足ComparableGang协定和IGang协定的compareTo()实现。

09-04 23:33