スーパータイプが期待されるサブクラスオブジェクトを引数として渡すことによるメソッドのオーバーライド


12

私はJavaを学んでいるだけで、実践的なプログラマーではありません。

私がフォローしている本は、メソッドをオーバーライドするとき、引数の型は同じでなければならないが、戻り値の型は多相的に互換性があると言っています。

私の質問は、オーバーライドメソッドに渡される引数が期待されるスーパータイプのサブクラスタイプになれないのはなぜですか?

オーバーロードメソッドでは、オブジェクトで呼び出すメソッドはすべて、オブジェクトで定義されることが保証されています。


推奨される重複に関するメモ:

最初の提案は、クラス階層、どこの機能を置くことを約あるように思われます。私の質問は、言語制限が存在する理由に焦点を当てています。

第二の提案は、私が尋ねるが、いないよ何をする方法を説明し、なぜそれがそのように行う必要があります。私の質問はその理由に焦点を合わせています。


1
前の質問で尋ねられ、答えられました:「これは、一般にリスコフ置換原理(LSP)に失敗したとみなされます。なぜなら、追加された制約により、基底クラスの操作が派生クラスに必ずしも適切ではないからです...」
gnat


@gnatの質問は、引数に対してより具体的なサブタイプを要求することがコンピューターの原則に違反するかどうかではなく、それが可能かどうかであり、edalorzoは答えました。
user949300 14

回答:


18

質問で最初に参照する概念は、共変戻り型と呼ばれます。

メソッドが特定の型のオブジェクトを返すことになっているため、共変の戻り型が機能し、オーバーライドするメソッドは実際にそのサブクラスを返す場合があります。Javaのような言語のサブタイピングルールに基づいて、SがのサブタイプであるT場合、T表示される場所はどこでもを渡すことができSます。

そのためS、を予期したメソッドをオーバーライドするときにを返すのは安全Tです。

メソッドのオーバーライドが、オーバーライドされたメソッドによって要求されたもののサブタイプである引数を使用することを受け入れるというあなたの提案は、型システムに不健全さをもたらすため、はるかに複雑です。

一方では、上記と同じサブタイピング規則により、おそらくあなたがしたいことに対してすでに機能しているでしょう。例えば

interface Hunter {
   public void hunt(Animal animal);
}

このクラスの実装は、あなたの質問の基準をすでに満たしているため、いかなる種類の動物も受け取れません。

しかし、あなたが提案したようにこのメソッドをオーバーライドできるとしましょう:

class MammutHunter implements Hunter {
  @Override
  public void hunt(Mammut animal) {
  }
}

ここに面白い部分があります、今、あなたはこれをすることができます:

AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh

AnimalHunterあなたの公開インターフェースによると、どんな動物も狩ることができるはずですが、あなたの実装によるMammutHunterと、Mammutオブジェクトのみを受け入れます。したがって、オーバーライドされたメソッドは、パブリックインターフェイスを満たしていません。ここで型システムの健全性を壊しました。

ジェネリックを使用して、必要なものを実装できます。

interface AnimalHunter<T extends Animal> {
   void hunt(T animal);
}

次に、MammutHunterを定義できます

class MammutHunter implements AnimalHunter<Mammut> {
   void hunt(Mammut m){
   }
}

また、一般的な共分散と反分散を使用すると、必要に応じてルールを緩和できます。たとえば、哺乳動物ハンターは特定のコンテキストでのみネコ科の動物を狩ることができます。

AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());

MammalHunterimplementsを想定していますAnimalHunter<Mammal>

その場合、これは受け入れられません。

hunter.hunt(new Mammut()):

マンマットが哺乳類の場合でも、ここで使用している反変種の制限により受け入れられません。したがって、あなたが言及したようなことをするために、型をある程度制御することができます。


3

問題は、引数として指定されたオブジェクトを使用してオーバーライドメソッドで行うことではありません。問題は、メソッドを使用するコードがメソッドにフィードできる引数のタイプです。

オーバーライドするメソッドは、オーバーライドされるメソッドの規約を満たす必要があります。オーバーライドされたメソッドが特定のクラスの引数を受け入れ、サブクラスのみを受け入れるメソッドでオーバーライドした場合、そのメソッドはコントラクトを満たしません。それが無効な理由です。

より具体的なタイプの引数でメソッドをオーバーライドすることを、オーバーロードと呼びます。同じ名前のメソッドを定義しますが、引数のタイプが異なるか、より具体的です。元のメソッドに加えて、オーバーロードされたメソッドを使用できます。どのメソッドが呼び出されるかは、コンパイル時に既知のタイプによって異なります。メソッドをオーバーロードするには、@ Overrideアノテーションを削除する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.