C ++とJava、またはそれらの尊重ルールでは、抽象メソッドのオーバーライドにどのような制限が課せられますか。引数または戻り型を一致させる必要があります。私は通常、戻り型のみで引数なしで実装された抽象関数を目にします。残りを指定するのは派生クラス次第です。それはどのように正確に機能しますか?
6 に答える
メソッドのオーバーライドには、オーバーライドする親メソッドと同じメソッド シグネチャが必要です。そうでない場合、オーバーライドとは呼ばれません。
ジャワ:
public abstract class AbstractTest {
public abstract void test() throws Exception;
}
public class ConcreteTest extends AbstractTest {
@Override
public void test() throws Exception {
}
}
ご覧のとおり、ConcreteTest
(を拡張するAbstractTest
) は をオーバーライドする必要がありますtest()
。メソッド名、戻り値の型は同じで、メソッド パラメータはありません。サブクラスは、基本クラスからスローされた例外を省略して、独自の例外をスローできます。サブクラスは、追加の (未) チェック例外を追加することもできます。
Peter Lawreyが述べたように、Java インターフェイス メソッドは暗黙的に抽象メソッドです ( Java Abstract Interfaceに関する私の SO の質問を参照してください)。
ここで重要なことは、この場合メソッドの可視性を変更できないことです (これは階層的な可視性、つまりプライベート -> 保護 -> パブリックであるため)。ただし、これは有効です。
public abstract class AbstractTest {
protected abstract void test() throws Exception;
}
public class ConcreteTest extends AbstractTest {
@Override
public void test() throws Exception {
}
}
(親には保護されたメソッドがあり、サブクラスは同じメソッドをオーバーライドでき、可視性の選択肢は保護またはパブリックの 2 つだけです)。
また、あなたが持っていると仮定します
public class B {
}
public class D extends B {
}
public abstract class Base {
public abstract B foo();
}
public class Derived extends Base {
@Override
public D foo() {
// TODO Auto-generated method stub
return new D();
}
}
ではなく aをDerived
返すことがわかります。何故ですか?これは、派生クラスが親クラスと同じシグネチャに従い、派生クラスの戻り値の型が親クラスの戻り値の型であるためです。D
B
subtype
だから、私はこれを持つことができます:
Base pureBase = new Derived();
B b = pureBase.foo(); //which returns class D
if (b instanceof D) {
//sure, it is, do some other logic
}
C++ では、共変の戻り型を使用して同様の効果を得ることができます。
C++
class AbstractTest {
public:
virtual void test() = 0;
};
class ConcreteTest : AbstractTest {
public:
void test() {
//Implementation here...
}
};
C++ では、純粋仮想関数(で終わる仮想関数=0
) を持つクラスは、Abstract クラスとして知られています。サブクラス (C++ では、クラス拡張子は で区切られ:
ます) は、純粋仮想メソッドをオーバーライドします (ただし、 は含まれません=0
)。親クラスと同じ署名があります。
Java の例に戻って、次のものがあるとします。
class B {
};
class D : B {
};
class Base {
public:
virtual B* foo() = 0;
}
class Derived : Base {
public:
D* foo() {
return new D();
}
}
同じ推論 (java で説明されている) がここで行われます。共変の戻り値の型は、保護されたプライベート継承でも機能します。共変の戻り値の型の詳細。
Java についてはわかりませんが、C++ ではまったく同じ引数の型を指定する必要があります。一方、戻り値の型は共変型です。つまり、元の関数で型 A へのポインターまたは参照が返された場合、オーバーライド関数は型 B へのポインターまたは参照を返すことができます。 A であるか、直接的または間接的に派生します。
Als が指摘したように、オーバーライドするには、関数を仮想として宣言する必要があります。virtual
OP は と の両方で定義されている抽象メソッドについて明示的に尋ねたので、これ=0
を指摘する必要はありません。ただし、オーバーライド関数を仮想として宣言する必要がないことを明確にしたいと思います。引用された標準が言うように、virtual と宣言された基本メンバー関数の署名 (共変型に関しては、緩和された規則を使用) に一致するメンバー関数は、virtual が指定されているかどうかに関係なく、オーバーライドされます。つまり、オーバーライド関数を仮想として宣言する必要はありません。一方、抽象メンバー関数はそうでなければなりません。
どちらの言語も、自然なセマンティクスの違いでオーバーライドするための要件に関して類似しています。基本的に、どちらも呼び出しコード(つまり引数)にまったく同じ制約を必要とし、処理に関して同じまたはより厳密な保証を提供します。これはここでは少し曖昧に聞こえるかもしれませんが、それを覚えておけば簡単です。
いつオーバーライドしますか
メンバー関数(メソッド)が基本クラスのメンバーをオーバーライドするには、両方の言語で関数がポリモーフィック( Javavirtual
ではなくC ++ final
)である必要があり、同じ名前と同じ番号の引数のタイプが必要です。一部の言語では、逆バリアントの引数タイプが許可されていますが、JavaもC++も許可されていません。
共変リターンタイプ
ここでの共変とは、戻り型の型が、メンバー関数が実装されている型と同じように変化することを意味します。つまり、派生関数によって返される型は、多型であり、同じであるか、基本クラスで宣言されている同じ型から派生している必要があります。Javaは参照言語であるため、プリミティブ型を除くすべての戻り型はポリモーフィズムを示す可能性があります。C ++は値言語であり、参照とポインターのみがポリモーフィックです。つまり、Javaでは、返される型は完全に一致するか、参照型であり、ベースによって返される型から派生する必要があります。C ++では、参照である必要がありますまたは同じ型または派生型へのポインタ。はじめにのように、その理由は、ベースを介してメンバー関数を呼び出すと、期待するものと一致するオブジェクトが得られるためです。
例外仕様
例外仕様はC++ではあまり一般的ではありませんが、Javaでは一般的です。どちらの言語でも、オーバーライドのアプローチは同じですが、派生クラスのオーバーライドメソッドには、スローできるものに関してより厳しい制約が必要です。ここでの言語の違いは、Javaがチェックされた例外のみを検証するため、ベースによってスローされなかった派生型でチェックされていない例外を許可するためです。一方、派生関数は新しいチェックを追加できません基本クラスに存在しない例外も、共分散が作用し、派生関数は共分散例外をスローする可能性があります。C ++では、例外仕様の意味はまったく異なりますが、同様に、派生型の仕様はベースよりも制約が厳しく、共変例外仕様も許可されます。
理論的根拠は同じです。try {} catch() {}
ベースタイプへの参照を介して呼び出しの周囲にブロックを記述し、ベースで宣言されているすべての例外をキャッチすると、オーバーライドの呼び出しではすべての例外が同じでキャッチされます。ブロック-Javaでチェックされていない可能性のある例外を除く。
アクセス修飾子
Javaでは、派生メソッドへのアクセス仕様は、少なくともベースのアクセス仕様と同じくらい制限的である必要があります。つまり、ベース関数の宣言でが指定されている場合protected
、派生関数はできませんがpublic
、private
興味深いことに、Javaはそうではありません。private
基本クラスの関数をオーバーライドできます。
C ++では、アクセス指定子はオーバーライドの対象にはなりません。アクセス指定子を必要に応じて変更して、派生クラスで多かれ少なかれ制限することができます。private
ちなみに、基本クラス(宣言されている)のメンバーをオーバーライドできます。これは、Javaのメソッドをvirtual
介して実装する必要があるNVIパターン(非仮想インターフェイス)を実装するために一般的に使用されます。protected
オーバーライドを停止します
final
Javaでは、メンバー関数をとしてマークするか、あるいはそれを作成することにより、任意のレベルでオーバーライドのチェーンを断ち切ることができますprivate
。C ++(現在の標準)では、オーバーライドしているメンバー関数に最終的なオーバーライドがアクセスできない場合でも、オーバーライドチェーンを切断することはできません。これにより、奇妙な効果が生じます。
struct base {
virtual void f() {}
};
struct derived : private base {
void g() {
f();
}
};
struct most_derived : derived {
void f() { // overrides base::f!!!
//base::f(); // even if it does not have accesss to it
}
};
その例では、継承はderived
レベルでプライベートでありmost_derived
、サブオブジェクトにアクセスできないため、base
その観点からは、 (内部でのコンパイルに失敗するbase
理由)から派生するのではなく、関数を実装することによって派生します。署名を使用すると、のオーバーライドが提供されます。オブジェクトのへの呼び出しはにディスパッチされ、オブジェクトのへの呼び出しはにディスパッチされます。base::f()
most_derived::f()
void ()
base::f
g()
most_derived
most_derived::f()
derived
base::f()
メソッドの署名 (戻り値の型、引数の型と数) は、派生クラスで基本クラスの署名と正確に一致する必要があります。そうしないと、派生クラスも抽象化されます。
例:
struct foo{
virtual void foobar( int myNum) = 0;
};
struct bar: foo{
int foobar(int myNum ){}
};
int main(){
foo *obj = new bar();
return 0;
}
test.cc:6: エラー: 'virtual int bar::foobar(int)' に指定された競合する戻り値の型<br> test.cc:2: エラー: 'virtual void foo::foobar(int)' をオーバーライドしています</p >
@Alsが述べたように、共変の戻り値の型は、戻り値の型が異なる可能性がある例外です。異なるとは、異なる型がそれぞれに型互換性があることを意味します。C++ の派生クラス型のポインター/参照は、基本型のポインター/参照と型互換性があります。
リンクからの例:
#include <iostream>
// Just create a class, and a subclass
class Foo {};
class Bar : public Foo {};
class Baz
{
public:
virtual Foo * create()
{
return new Foo();
}
};
class Quux : public Baz
{
public:
// Different return type, but it's allowed by the standard since Bar
// is derived from Foo
virtual Bar * create()
{
return new Bar();
}
};
int main()
{
Quux *tmp = new Quux();
Bar *bar = tmp->create();
return 0;
}
ジャワ:
abstract class MyAbstract {
abstract String sayHelloTo(String name);
}
final class SayEnglish extends MyAbstract {
@Override
public String sayHelloTo(String name) {
return "Hello, " + name + "!";
}
}
final class SayLatin extends MyAbstract {
@Override
public String sayHelloTo(String name) {
return "Lorem, " + name + "!";
}
}
同じことは、構文の違いを考慮した C++ の場合です。つまり、オーバーライドされた抽象メソッドの同じシグネチャです。
Java のオーバーライド メソッドには、オーバーライドする抽象メソッドと同じ署名が必要です。また、親クラス以上にアクセスを制限することはできません。http://download.oracle.com/javase/tutorial/java/IandI/override.htmlを参照してください。
あなたはC++を意味していると思います。Java と同じように、オーバーライド メソッドのシグネチャはオーバーライドされたものと一致する必要があります。http://www.learncpp.com/cpp-tutorial/126-pure-virtual-functions-abstract-base-classes-and-interface-classes/を参照してください。
Wiki にもページがあります en.wikipedia.org/wiki/Method_overriding. 抽象メソッドはパラメーターを持つことができます。それに制限はありません。多くの場合、パラメーターを渡すことは意味がありません。お役に立てれば :)