静的メソッドをオーバーライドできないのはなぜですか?


13

この質問への回答で、一般的なコンセンサスは、静的メソッドはオーバーライドされることを意図していないということでした(したがって、C#の静的関数は仮想または抽象にできません)。ただし、これはC#の場合だけではありません。Javaもこれを禁止しており、C ++もそれを好まないようです。ただし、子クラスでオーバーライドしたい静的関数(ファクトリーメソッドなど)の多くの例を考えることができます。理論的には、それらを回避する方法がありますが、それらはどれもきれいでも単純でもありません。

静的関数をオーバーライドできないのはなぜですか?


4
Delphiはクラスメソッドをサポートしています。これは、静的メソッドによく似ており、オーバーライドできます。selfクラスのインスタンスではなく、クラスを指すポインターが渡されます。
CodesInChaos

静的の定義の1つは、変更がないことです。オーバーライドしたい関数を静的にした理由を知りたいです。
-JeffO

4
@JeffO:「static」の英語の定義は、静的メンバー関数の固有のセマンティクスとはまったく関係ありません。
モニカとの軽さレース

5
あなたの質問は、「なぜ静的メソッドは単一仮想ディスパッチではなく静的に型指定れたディスパッチを使用すべきなのか?」です。あなたが質問をそのように言い表すとき、私はそれが答えると思う。
エリックリッパー

1
@EricLippert質問は「C#がクラスメソッドの代わりに静的メソッドを提供するのはなぜですか」と表現する方が良いかもしれませんが、それでも有効な質問です。
-CodesInChaos

回答:


13

静的メソッドでは、オーバーライドメカニズムの適切な制御を提供するオブジェクトはありません。

通常のクラス/インスタンス仮想メソッドメカニズムでは、次のようにオーバーライドを細かく制御できます。各実オブジェクトは、正確に1つのクラスのインスタンスです。そのクラスは、オーバーライドの動作を決定します。仮想メソッドで常に最初の亀裂を取得します。その後、実装に適したタイミングで親メソッドを呼び出すことを選択できます。また、各親メソッドは、親メソッドを呼び出す順番を取得します。これにより、親の呼び出しがうまくカスケードされ、オブジェクト指向が知られているコードの再利用という概念の1つが実現します。(ここでは、ベース/スーパークラスのコードが比較的複雑な方法で再利用されています。OOPでのコード再利用の別の直交概念は、同じクラスの複数のオブジェクトを単に持つことです。)

基本クラスはさまざまなサブクラスで再利用でき、それぞれが快適に共存できます。独自の動作を指示するオブジェクトをインスタンス化するために使用される各クラスは、他のクラスと平和的に同時に共存します。クライアントは、オブジェクトをインスタンス化し、必要に応じて他のクラスに渡すために使用するクラスを選択することにより、必要な動作とタイミングを制御できます。

(これは完璧なメカニズムではありません。もちろん、サポートされていない機能を常に識別できるため、ファクトリメソッドや依存性注入などのパターンが最上位にある理由です。)

したがって、他に何も変更せずに静的変数のオーバーライド機能を作成すると、オーバーライドの順序付けが困難になります。オーバーライドの適用可能性について限定されたコンテキストを定義するのは難しいので、オブジェクトのようにローカルではなくグローバルにオーバーライドを取得します。動作を切り替えるインスタンスオブジェクトはありません。だから、誰かがたまたま別のクラスによってオーバーライドされた静的メソッドを呼び出した場合、オーバーライドは制御を取得する必要がありますか?そのようなオーバーライドが複数ある場合、誰が最初に制御を取得しますか?第二?インスタンスオブジェクトのオーバーライドでは、これらの質問にはすべて意味のある適切な回答がありますが、静的な質問にはありません。

静的のオーバーライドは実質的に無秩序であり、このようなことは以前に行われました。

たとえば、Mac OS System 7以前では、オペレーティングシステムの前にアプリケーションで作成されたシステムコールを制御することにより、システムを拡張するトラップパッチメカニズムを使用していました。システムコールパッチテーブルは、単一のグローバルテーブルであることを除いて、インスタンスオブジェクトのvtableによく似た関数ポインタの配列と考えることができます。

これは、トラップパッチの順序付けられていない性質のため、プログラマーに計り知れない悲嘆を引き起こしました。基本的に最後にトラップにパッチを当てた人は、たとえそれを望まなかったとしても、基本的に勝ちました。トラップの各パッチは、ある種の親呼び出し機能の以前のトラップ値をキャプチャしますが、これは非常に脆弱です。トラップパッチを削除します。たとえば、パッチを削除するために必要な情報が実際にはなかったため、システムコールについて知る必要がなくなった場合は、悪いフォームと見なされます。君は)。

これは、静的のオーバーライドのメカニズムを作成することが不可能だと言うことではありませんが、代わりに静的フィールドと静的メソッドをインスタンスフィールドとメタクラスのインスタンスメソッドに変えて、通常のオブジェクトがオリエンテーション技術が適用されます。これを行うシステムもあることに注意してください。CSE341:Smalltalkクラスとメタクラス。関連項目:Javaのstaticに相当するSmalltalkとは何ですか?


私はあなたがそれを合理的にうまく機能させるためにあなたがいくつかの深刻な言語機能設計をしなければならないだろうと言っている。一例として、素朴なアプローチが行われ、リンプが行われましたが、非常に問題が多く、不完全で使いにくい抽象化を提供することにより、おそらくアーキテクチャ的に欠陥があります。

静的オーバーライド機能がうまく機能するように設計し終えた頃には、クラスベースのメソッドへの/のためのOOPの自然な拡張であるメタクラスの何らかの形を発明したかもしれません。したがって、これを行わない理由はありません。一部の言語では実際に行われます。おそらく、多くの言語がそうしないことを選択することは、もう少し余分な要件です。


私が正しく理解している場合:理論的にあなたがこのようなことをするか、またはする必要があるかどうかの問題ではありませんが、プログラム的には、それの実装は難しく、必ずしも有用ではありませんか?
PixelArtDragon

@Garan、私の補遺をご覧ください。
エリックエイド

1
私は注文の議論を買いません。メソッドを呼び出したクラスが提供するメソッド(または、言語の選択した線形化に従ってメソッドを提供する最初のスーパークラス)を取得します。
ホッブズ

1
@PierreArlaud:しかしthis.instanceMethod()、動的解像度をself.staticMethod()持っているので、同じ解像度、つまり動的であれば、静的メソッドではなくなります。
ヨルグWミットタグ

1
静的メソッドのオーバーライドセマンティクスを追加する必要があります。架空のJava + metaclassesは何も追加する必要はありません!クラスオブジェクトを作成するだけで、静的メソッドは通常のインスタンスメソッドになります。オブジェクト、クラス、インスタンスメソッドなどの既存の概念のみを使用するため、言語に何かを追加する必要はありません。おまけとして、実際には言語から静的メソッドを削除できます!言語から概念と複雑さを削除することで、機能を追加できます!
ヨルグWミットタグ

9

オーバーライドは仮想ディスパッチに依存しthisます。パラメータのランタイムタイプを使用して、呼び出すメソッドを決定します。静的メソッドにはthisパラメーターがないため、ディスパッチするものはありません。

一部の言語、特にDelphiとPythonには、これを可能にする「中間」スコープがあります:クラスメソッド。 クラスメソッドは通常のインスタンスメソッドではありませんが、静的でもありません。そのタイプのインスタンスではなく、オブジェクトタイプ自体への参照であるselfパラメーター(両方の言語がthisパラメーターを呼び出しますself)を受け取ります。その値を使用して、仮想ディスパッチを実行するためのタイプを使用できるようになりました。

残念ながら、JVMもCLRも同等のものはありません。


それはself、オーバーライド可能なメソッド(もちろん、Smalltalk、Ruby、Dart、Objective C、Self、Python)を持つインスタンス化された実際のオブジェクトとしてクラスを持つ言語を使用していますか?リフレクションアクセスがあったとしても、クラスがファーストクラスオブジェクトではないC ++の後継言語と比較して?
Jerry101

明確にするために、PythonまたはDelphiでは、クラスメソッドをオーバーライドできると言っていますか?
エリックエイド

@ErikEidt Pythonでは、ほとんどすべてがオーバーライド可能です。Delphiでは、virtualインスタンスメソッドと同様に、クラスメソッドを明示的に宣言し、子孫クラスでオーバーライドできます。
メイソンウィーラー

それで、これらの言語はメタクラスのある種の概念を提供しているのですか?
エリックエイド

1
@ErikEidt Pythonには「真の」メタクラスがあります。Delphiでは、クラス参照は特別なデータ型であり、それ自体はクラスではありません。
メイソンウィーラー

3

あなたが尋ねる

静的関数をオーバーライドできないのはなぜですか?

お願いします

なぜオーバーライドしたい関数は静的なのですか?

特定の言語では、静的な方法でショーを開始する必要があります。しかし、その後、静的メソッドをまったく使用しなくても、非常に多くの問題を本当に解決できます。

オブジェクトの状態に依存しない場合は常に静的メソッドを使用したい人もいます。他のオブジェクトの構築に静的メソッドを使用したい人もいます。静的メソッドを可能な限り避けることを好む人もいます。

これらの人々は誰も間違っていません。

オーバーライド可能にする必要がある場合は、静的なラベル付けを停止してください。ステートレスオブジェクトが飛んでいるので、何も壊れません。


言語が構造体をサポートしている場合、ユニットタイプはサイズがゼロの構造体で、論理エントリメソッドに渡されます。そのオブジェクトの作成は、実装の詳細です。実装の詳細を実際の真実として話し始めると、実世界の実装では、サイズがゼロの型を実際にインスタンス化する必要がないことも考慮する必要があると思います(ロードするための指示は必要ないため)またはそれを保存します)。
セオドロスチャツィジアンナキス

さらに、「舞台裏」で(たとえば実行可能レベルで)話している場合、環境引数は言語の型として定義でき、プログラムの「実際の」エントリポイントは次のインスタンスメソッドになります。そのタイプは、OSローダーによってプログラムに渡されたインスタンスに作用します。
セオドロスチャツィジアンナキス

それはあなたの見方にかかっていると思います。たとえば、プログラムが開始されると、OSはそのプログラムをメモリにロードし、プログラムのバイナリエントリポイントの個々の実装が存在するアドレス(_start()Linux のシンボルなど)に移動します。その例では、実行可能ファイルはオブジェクト(独自の「ディスパッチテーブル」とすべてを含む)であり、エントリポイントは動的にディスパッチされる操作です。したがって、プログラムのエントリポイントは、外側から見ると本質的に仮想であり、内側から見ると簡単に仮想的に見えるようにすることもできます。
セオドロスチャツィジアンナキス

1
「ステートレスなオブジェクトが飛び回っているので、何も壊れません。」++++++
ラバーダック

@TheodorosChatzigiannakisあなたは強い議論をします。他に何もなければ、この線は私が意図した思考線を刺激しないと確信しました。私は、人々が盲目的に静的メソッドに関連付けている習慣的な慣習のいくつかを肩代わりする許可を人々に与えようとしていました。だから私は更新しました。考え?
candied_orange

2

静的メソッドをオーバーライドできないのはなぜですか?

「すべき」という問題ではありません。

「オーバーライド」とは、「動的にディスパッチする」ことを意味します。「静的メソッド」とは、「静的にディスパッチする」ことを意味します。何かが静的である場合、上書きできません。何かをオーバーライドできる場合、それは静的ではありません。

あなたの質問は、「三輪車がなぜ四輪を持つべきではないのか」という質問に似ています。「三輪車」の定義は、3つの車輪があります。三輪車の場合、4つの車輪を持つことはできません。4つの車輪がある場合、三輪車になることはできません。同様に、「静的メソッド」の定義は、静的にディスパッチされることです。静的メソッドの場合、動的にディスパッチすることはできません。動的にディスパッチすることができる場合、静的メソッドにすることはできません。

もちろん、オーバーライドできるクラスメソッドを持つことは完全に可能です。または、Rubyのような言語を使用することもできます。クラスでは、クラスは他のオブジェクトとまったく同じようにオブジェクトであり、インスタンスメソッドを持つことができます。(Rubyには、インスタンスメソッドが1種類しかありません。クラスメソッド、静的メソッド、コンストラクタ、関数、またはプロシージャはありません。)


1
「定義による」
candied_orange

1

したがって、C#の静的関数は仮想または抽象にすることはできません

C#では、常にクラスを使用して静的メンバーを呼び出します(例:BaseClass.StaticMethod()not)baseObject.StaticMethod()。したがって、ChildClassから継承しBaseClasschildObjectのインスタンスがある場合、ChildClassから静的メソッドを呼び出すことはできませんchildObject。常に実際のクラスを明示的に使用する必要があるため、a static virtualは無意味です。

できることはstatic、子クラスで同じメソッドを再定義し、newキーワードを使用することです。

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

を呼び出すことができた場合baseObject.StaticMethod()、あなたの質問は理にかなっているでしょう。

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