9

我有一个新手问题。

interface Animal {
    void partner(Animal other);
}

class Lion implements Animal {
    int areaUnderControl;
    @Override
    public void partner(Animal other) {
        Lion lion = (Lion) other;
        this.areaUnderControl += lion.areaUnderControl; 
    }
}

class Human implements Animal {
    int money;
    @Override
    public void partner(Animal other) {
        Human human = (Human) other;
        this.money += human.money;
    }
}

我希望具体实现中的合作伙伴方法只接收其类型或其子类型的参数。一种解决方案是使用泛型将 Animal 定义为:

interface Animal<T extends Animal<T>> {
    void partner(T other);
}

class Lion implements Animal<Lion> {
    int areaUnderControl;
    @Override
    public void partner(Lion other) {
        this.areaUnderControl += other.areaUnderControl; 
    }
}

class Human implements Animal<Human> {
    int money;
    @Override
    public void partner(Human other) {
        this.money += other.money;
    }
}

但是有人仍然可以滥用它作为

class Bear implements Animal<Lion> {
    int honey;
    @Override
    public void partner(Lion other) {
        this.honey += other.areaUnderControl; //Problem
    }
}

熊渴望与狮子合作。有没有办法可以将它与这个的类型和子类型联系起来

我尝试搜索现有的问题,不知何故我觉得界面本身的设计可能存在缺陷。如果有人能指出为什么这是错误的或实现这一目标的正确方法,将不胜感激。

谢谢!

4

5 回答 5

2

没有办法在接口级别强制执行此操作,因为从类型安全的角度来看没有理由这样做。那么如果可以定义 aclass Bear implements Animal<Lion>呢?就其本身而言,它是类型安全的(即不会抛出意外的 ClassCastException 等)——这就是 Generics 的全部意义所在。

事实上,从类型安全的角度来看,您很可能只需要interface Animal<T>(类似于interface Comparable<T>)。是的,这将允许某人使用完全不相关的类的类型参数来定义实现 Animal 的奇怪类。所以呢?这本身并没有错。

使用此接口的地方,他们可以强制执行您想要的关系,例如public <T extends Animal<? super T>> void someMethodThatUsesAnimal(T animal). 因为只有在使用它的地方才真正关心partner方法是否将自身作为参数。这类似于如何Comparable使用(例如public <T extends Comparable<? super T>> void sort(List<T> list)

于 2012-07-11T18:03:30.000 回答
1

我认为您不能在编译时强制执行此操作。请查看在运行时确保这一点的解决方法。

class Tester {
    public static void main(String[] args) {
        SnowLion lion = new SnowLion();
        // This will throw IllegalArgumentException
        Bear bear = new Bear();
    }
}

abstract class Animal<T extends Animal<T>> {

    protected Animal(Class<T> implClazz) {
        if (!implClazz.isAssignableFrom(getClass())) {
            throw new IllegalArgumentException();
        }
    }

    abstract void partner(T other);
}

class Lion extends Animal<Lion> {

    public Lion() {
        super(Lion.class);
    }

    int areaUnderControl;

    @Override
    public void partner(Lion other) {
        this.areaUnderControl += other.areaUnderControl;
    }
}

class Human extends Animal<Human> {
    public Human() {
        super(Human.class);
    }

    int money;

    @Override
    public void partner(Human other) {
        this.money += other.money;
    }
}

class Bear extends Animal<Lion> {

    public Bear() {
        super(Lion.class);
    }

    int honey;

    @Override
    public void partner(Lion other) {
        this.honey += other.areaUnderControl; // Problem
    }
}

class SnowLion extends Lion {

    int snowAreaUnderControl;

    public SnowLion() {
        super();
    }

    @Override
    public void partner(Lion other) {
        this.snowAreaUnderControl += other.areaUnderControl;
    }
}

我从Java 泛型中得到了这个想法来强制执行抽象方法的返回类型

希望这可以帮助。

于 2012-07-11T06:36:33.353 回答
0

每次有人将Animal对象作为参数传递给方法时,您都可以强制执行此操作。例如,如果您在某处维护动物园中所有动物的列表,则可以扩展列表的类并add()使用此通用版本覆盖其方法:

@Override
public <AnimalType extends Animal<AnimalType>> void add(AnimalType animal) {
    super.add(animal);
}

此方法将接受任何Animal<itself>,但不接受Animal<something else>。这样,某人仍然可以定义 aBear extends Animal<Lion>但您的动物园将不再允许该熊,因为如果您将其作为参数传递给add()该调用将引发编译时绑定不匹配错误。

更棘手的是对方法的返回类型强制执行相同的约定。截至目前,该判决仍未判决:

强制 Java 方法返回类型以适应某些通用签名

于 2012-11-30T16:28:52.723 回答
0

newacct 对这个问题给出了一个非常好的答案,并将其类比为可比较的。确保合作的两个对象的职责是委托给使用地点,即。在这种情况下的动物。为了清楚起见,这里用示例代码说明:

interface Animal<T> {
    void partner(T other);
}

class Lion implements Animal<Lion> {
    int areaUnderControl;

    @Override
    public void partner(Lion other) {
        this.areaUnderControl += other.areaUnderControl;
    }
}

class Human implements Animal<Human> {
    int money;

    @Override
    public void partner(Human other) {
        this.money += other.money;
    }
}

// Naughty bear
class Bear implements Animal<Lion> {
    int honey;

    @Override
    public void partner(Lion other) {
        this.honey += other.areaUnderControl;// problem
    }
}

class Animals {
    static <T extends Animal<? super T>> void partner(T animal1, T animal2) {
        animal1.partner(animal2);
    }
}


public class CaptureGenericsFunTest {

public static void main(String[] args) {
    Lion lion1 = new Lion();
    lion1.areaUnderControl = 10;
    Lion lion2 = new Lion();
    lion2.areaUnderControl = 20;
    Bear bear = new Bear();
    bear.honey = 100;

    Animals.partner(lion1, lion2); // ok
    Animals.partner(lion1, bear); // compilation problem
}
}
于 2012-07-13T06:27:33.067 回答
0

你也可以像这样定义你的界面:

interface Animal<T extends Animal<T>> {
    void partner(T other);

    /**
     * Please implement with just this line in it:<br/><br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<code>return this;</code>
     * @return <code>this</code>
     */
    T returnThis();
}

那么任何实现这个类的人都必须提供一个方法来返回指定动物类型的对象。因此,您的假熊必须提供Lion returnThis(),如果他遵循文档并将其简单地实现为return this,则会引发类型不匹配错误。

这仍然不能防止故意弄乱事物然后返回 anew Lion()而不是 的人this,但它肯定会捕获错误,您只是尝试复制Bear extends Animal<Bear>类来创建类Grizzly而忘记将结果更改Grizzly extends Animal<Bear>Grizzly extends Animal<Grizzly>. (如果你真的想让灰熊与黑熊合作是你动物园董事会必须决定的另一个问题......)

于 2012-11-30T17:40:51.577 回答