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;
    }
}

具体的な実装のパートナーメソッドは、そのタイプまたはサブタイプのパラメーターのみを受け取るようにします。1つの解決策は、ジェネリックを使用して動物を次のように定義することです。

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

型安全性の観点からそうする理由がないため、インターフェースレベルでこれを強制する方法はありません。では、を定義することが可能であるとしたらどうclass Bear implements Animal<Lion>でしょうか?それ自体はタイプセーフです(つまり、予期しないClassCastExceptionなどをスローしません)。これがGenericsの目的です。

実際、型安全性の観点からは、おそらく必要なだけですinterface Animal<T>(と類似していinterface Comparable<T>ます)。はい、これにより、完全に無関係なクラスであるtypeパラメーターを使用して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

newacctは、この質問に対して非常に優れた回答を提供し、同等のものとの類似性を示しました。提携するための2つのオブジェクトが使用場所に委任されていることを確認する義務。この場合の動物。わかりやすくするために、ここにサンプルコードを示します。

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

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()がスローされるためです。

はるかに難しいのは、メソッドの戻り型にこれと同じ規則を適用することです。評決は今のところまだその1つに出ています:

特定の汎用シグネチャに適合するようにJavaメソッドの戻り型を強制する

于 2012-11-30T16:28:52.723 に答える
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をコピーしてBear extends Animal<Bear>クラスを作成しようとし、結果をGrizzlyに変更するのを忘れたというエラーを確実にキャッチします。(実際にグリズリーをツキノワグマと組むことを許可したい場合は、動物園の理事会が決定しなければならないもう1つの問題です...)Grizzly extends Animal<Bear>Grizzly extends Animal<Grizzly>

于 2012-11-30T17:40:51.577 に答える