使用java泛型来确保接收的参数与其类或子类型相同

时间:2012-07-11 06:07:52

标签: java generics

我有一个新手问题。

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
    }
}
熊正渴望与狮友合作。在某种程度上,我可以将它与 this

的类型和子类型联系起来

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

谢谢!

5 个答案:

答案 0 :(得分:2)

无法在接口级别强制执行此操作,因为没有理由从类型安全的角度来执行此操作。那么,如果可以定义class 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)

答案 1 :(得分: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 generics to enforce return type of abstract method

得到了这个想法

希望这会有所帮助。

答案 2 :(得分: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
}
}

答案 3 :(得分:0)

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

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

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

对于方法的返回类型强制执行相同的约定更为棘手。到目前为止,判决结果尚未结束:

Enforcing Java method return type to fit certain Generic signature

答案 4 :(得分: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,那将导致类型不匹配错误。

这仍然不能防止那些故意想弄乱事物然后返回new Lion()而不是this的人,但它肯定会发现你只是试图复制{{}}的错误{1}}类要制作课程Bear extends Animal<Bear>而忘记将结果Grizzly更改为Grizzly extends Animal<Bear>。 (如果你真的想让灰熊与黑熊合作是动物园董事会必须决定的另一个问题......)