5

1行を除いて、非常によく似たメソッドを使用する2つのオブジェクトがあります。例えば:

public class Cat extends Animal
public class Dog extends Animal

そして、どちらもbreed抽象クラスのメソッドを使用しAnimalます。1つはを呼び出しnew Dog()、もう1つはを呼び出しますnew Cat()。今abstract public void breed();はAnimalのように宣言していますが、それを一般化して、オーバーライドするための抽象的なメソッドにする必要がないようにする方法はありますか?

4

3 に答える 3

3

実際、はい、できます。パフォーマンスが少し不安定になる可能性があるため、リフレクションを使用する必要がありますが、これは(テストされていない)機能するはずです。

public abstract class Animal{
    public Animal breed(){
      return getClass().newInstance();
    }
    //other methods
}

これにより、Animalのタイプ(実装されている場所)ではなく、実際の呼び出しタイプの新しいインスタンスが返されます。

これは実際にはプロトタイプパターンにいくぶん似ています。この場合、既存のインスタンスをコピーするのではなく、新しいインスタンスを作成します。

編集

@FrankPavageauがコメントで指摘したように、コンストラクターで例外をマスクするのではなく、次を使用して同じ結果を得ることができます。

public abstract class Animal{
    public Animal breed(){
      return getClass().getConstructor().newInstance();
    }
    //other methods
}

これは、スローされた例外をラップします。InvocationTargetExceptionこれは、少しクリーンで、おそらくデバッグが簡単です。その提案をしてくれた@FrankPavageauに感謝します。

于 2012-09-22T18:30:53.130 に答える
3

breedあなたが「私の子供を作る」という意味だと仮定すると、これを行うには多くの方法があります。

反射

まず、反射を使用します。クラスに引数なしのコンストラクターがある場合、これはClass.newInstanceを呼び出すのと同じくらい簡単です。

public Animal breed() {
    try {
        return (Animal) getClass().newInstance();
    } catch (Exception ex) {
        // TODO Log me
        return null;
    }
}

すべてのサブクラスに引数なしのコンストラクターがない場合は、すべてのサブクラスに統一されたコンストラクターが必要です。たとえば、とがある場合はCat(int, String)Dog(int, String)を介してコンストラクタを取得し、それをClass.getConstructor呼び出す必要がありますnewInstance

return (Animal) getClass().getConstructor(int.class, String.class).newInstance(0, "Unnamed");

intStringたとえば、ここには年齢と名前があります。これは、リフレクションを使用してこれを行う方法です。

プロバイダー

別の方法は、この単純なインターフェースを使用することです。

public interface Provider<T> {
    T create();
}

次に、抽象クラスにコンストラクターでこのインスタンスを取得させます。

public abstract class Animal {
    private final Provider<Animal> animalProvider;

    protected Animal( ... , Provider<Animal> animalProvider) {
        // ...
        this.animalProvider = animalProvider;
    }

    public Animal breed() {
        return animalProvider.create();
    }
}

次に、サブクラスはProvider<Animal>、サブクラスの新しいインスタンスを作成するスーパークラスにを渡します。

public class Dog extends Animal {
    public Dog( ... ) {
        super( ... , new DogProvider());
        // ...
    }

    private static class DogProvider implements Provider<Animal> {
        public Animal create() {
            return new Dog( ... );
        }
    }
}

他のサブクラスについても同じようにします。

注:breed「私のタイプを取得する」という意味の場合は、質問を編集してそのようにする必要があります。これがあなたの意図したことである場合、これは実行可能な解決策です。

public abstract class Animal {
    protected final Breed breed;

    protected Animal( ... , Breed breed) {
        // ...
        this.breed = breed;
    }

    public Breed getBreed() {
        return breed;
    }
}

データコンテナメソッドのget/規則に従うことをお勧めします。setJavaには、これらの命名規則を処理するように設計されたBeanクラスがあり、多くのプラットフォームで多かれ少なかれ標準となっています。サブクラスの場合:

public class Dog extends Animal {
    public Dog( ... ) {
        super( ... , new Breed( ... ));
        // ...
    }
}
于 2012-09-22T18:53:22.603 に答える
2

いいえ、ありません。あなたはあなたが以下のようにしたことのような何かを持っている必要があります

抽象クラスに入れたいと思います

public abstract Breed getBreed();

そして、各サブクラスで

public Breed getBreed() {
    return new DogBreed();
}

それに似たような猫が戻ってきます。

また

品種と呼ばれる動物クラスの保護されたフィールドを持っています。次に、これを各サブクラスで初期化できます。これにより、抽象メソッドが不要になります。例えば

public abstract class Animal {
    Breed breed;
...
}

そして犬に

public class Dog extends Animal {
    public Dog() {
        breed = new DogBreed();
    }
}

猫に似たものがあります。

モデルを1種類の犬だけに制限するのではなく、さまざまな犬種の犬オブジェクトを作成できるように、犬種を犬/猫の飼い主に渡すことも価値があるかもしれません。

あなたの例では、品種が必ずしも正しくモデル化されているかどうかはわかりません。あなたは本当に新しいDog()を品種にしたいですか?それともタイプですか?その場合、それは単なる動物であり、動物を返す抽象的なメソッドが進むべき道です。

于 2012-09-22T18:25:45.020 に答える