静的メソッドをオーバーライドできないのはなぜですか?
可能であれば、例を使用してください。
オーバーライドは、クラスのインスタンスを持つことに依存します。ポリモーフィズムのポイントは、クラスをサブクラス化でき、それらのサブクラスを実装するオブジェクトは、スーパークラスで定義された(およびサブクラスでオーバーライドされた)同じメソッドに対して異なる動作をすることです。静的メソッドはクラスのどのインスタンスにも関連付けられていないため、この概念は適用できません。
これに影響を与えたJavaの設計を推進する2つの考慮事項がありました。1つは、パフォーマンスに関する懸念でした。Smalltalkが遅すぎる(ガベージコレクションと多態的な呼び出しがその一部である)という批判が多く、Javaの作成者はそれを回避することを決意しました。もう1つは、Javaの対象読者がC++開発者であるという決定でした。静的メソッドをそのように機能させることは、C ++プログラマーにとってなじみのある利点があり、どのメソッドを呼び出すかを実行するまで待つ必要がないため、非常に高速でした。
個人的には、これはJavaの設計上の欠陥だと思います。はい、はい、静的メソッドがクラスなどにアタッチされているのに対し、非静的メソッドはインスタンスにアタッチされていることを理解しています。それでも、次のコードを検討してください。
public class RegularEmployee {
private BigDecimal salary;
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".02");
}
public BigDecimal calculateBonus() {
return salary.multiply(getBonusMultiplier());
}
/* ... presumably lots of other code ... */
}
public class SpecialEmployee extends RegularEmployee {
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".03");
}
}
このコードは、期待どおりに機能しません。つまり、SpecialEmployeeは、通常の従業員と同じように2%のボーナスを獲得します。ただし、「静的」を削除すると、SpecialEmployeeは3%のボーナスを獲得します。
(確かに、この例は、実際にはボーナス乗数をハードコーディングするのではなく、データベースのどこかに配置したいという点で、コーディングスタイルが貧弱です。しかし、それは、例をたくさん詰め込みたくなかったからです。ポイントに関係のないコードの。)
getBonusMultiplierを静的にしたいと思うかもしれないことは私には非常にもっともらしいようです。おそらく、各カテゴリに従業員のインスタンスがなくても、すべてのカテゴリの従業員のボーナス乗数を表示できるようにする必要があります。そのような例のインスタンスを検索するポイントは何でしょうか?新しいカテゴリの従業員を作成していて、まだ従業員が割り当てられていない場合はどうなりますか?これは非常に論理的に静的な関数です。
しかし、それは機能しません。
そして、はい、はい、私はそれを機能させるために上記のコードを書き直すための多くの方法を考えることができます。私の言いたいことは、それが解決できない問題を引き起こすということではなく、合理的な人が期待するように言語が動作しないため、不注意なプログラマーに罠をかけるということです。
おそらく、OOP言語用のコンパイラーを作成しようとすると、静的関数をオーバーライドできるようにコンパイラーを実装することが困難または不可能である理由がすぐにわかります。
あるいは、Javaがこのように動作するのにはいくつかの理由があります。誰かがこの振る舞いの利点、これによって簡単になる問題のいくつかのカテゴリーを指摘できますか?つまり、Java言語の仕様を指摘して、「これはその動作が文書化されている」と言うだけではありません。そんなこと知ってる。しかし、それがこのように動作する必要がある理由はありますか?(明らかな「正しく機能させるのは難しすぎた」ことに加えて...)
アップデート
@VicKirk:Javaが統計を処理する方法に適合しないため、これが「悪い設計」であるという意味の場合、私の回答は「もちろんです」です。元の投稿で言ったように、それは機能しません。しかし、これが機能する言語、つまり仮想関数のように静的なものをオーバーライドできる言語に根本的な問題があるという意味で設計が悪いということを意味する場合、これは何らかの形であいまいさをもたらすか、不可能になります効率的に実装するなど、「なぜ?コンセプトの何が問題なの?」と答えます。
私が挙げた例は、とても自然なことだと思います。インスタンスデータに依存しない関数を持つクラスがあり、インスタンスから独立して呼び出したい場合や、インスタンスメソッド内から呼び出したい場合があります。なぜこれが機能しないのですか?私は何年にもわたってこの状況にかなりの回数遭遇しました。実際には、関数を仮想化してから、静的メソッドを作成することで回避します。静的メソッドの唯一の目的は、ダミーインスタンスを使用して仮想メソッドに呼び出しを渡す静的メソッドです。それはそこにたどり着くための非常に回りくどい方法のようです。
簡単に言えば、それは完全に可能ですが、Javaはそれを行いません。
Javaの現在の状況を示すコードを次に示します。
ファイルBase.java
:
package sp.trial;
public class Base {
static void printValue() {
System.out.println(" Called static Base method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Base method.");
}
void nonLocalIndirectStatMethod() {
System.out.println(" Non-static calls overridden(?) static:");
System.out.print(" ");
this.printValue();
}
}
ファイルChild.java
:
package sp.trial;
public class Child extends Base {
static void printValue() {
System.out.println(" Called static Child method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Child method.");
}
void localIndirectStatMethod() {
System.out.println(" Non-static calls own static:");
System.out.print(" ");
printValue();
}
public static void main(String[] args) {
System.out.println("Object: static type Base; runtime type Child:");
Base base = new Child();
base.printValue();
base.nonStatPrintValue();
System.out.println("Object: static type Child; runtime type Child:");
Child child = new Child();
child.printValue();
child.nonStatPrintValue();
System.out.println("Class: Child static call:");
Child.printValue();
System.out.println("Class: Base static call:");
Base.printValue();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
child.localIndirectStatMethod();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
child.nonLocalIndirectStatMethod();
}
}
これを実行すると(私はMacで、Eclipseから、Java 1.6を使用して実行しました)、次のようになります。
Object: static type Base; runtime type Child.
Called static Base method.
Called non-static Child method.
Object: static type Child; runtime type Child.
Called static Child method.
Called non-static Child method.
Class: Child static call.
Called static Child method.
Class: Base static call.
Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
Non-static calls own static.
Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
Non-static calls overridden(?) static.
Called static Base method.
ここでは、驚くかもしれない(そして質問が関係している)唯一のケースが最初のケースであるように見えます:
「実行時型は、オブジェクトインスタンス()で呼び出された場合でも、呼び出される静的メソッドを決定するために使用されませんobj.staticMethod()
。」
そして最後のケース:
「クラスのオブジェクトメソッド内から静的メソッドを呼び出す場合、選択される静的メソッドは、オブジェクトのランタイムタイプを定義するクラスからではなく、クラス自体からアクセスできるメソッドです。」
静的呼び出しはコンパイル時に解決されますが、非静的メソッド呼び出しは実行時に解決されます。静的メソッドは(親から)継承されますが、(子によって)オーバーライドされないことに注意してください。そうでなければ、これは驚きかもしれません。
オブジェクトメソッド呼び出しは実行時型を使用して解決されますが、静的(クラス)メソッド呼び出しはコンパイル時(宣言済み)型を使用して解決されます。
これらのルールを変更して、例の最後の呼び出しが呼び出されるようにChild.printValue()
するには、コンパイラがコンパイル時にオブジェクトの宣言されたクラスを使用して呼び出しを解決するのではなく、静的呼び出しに実行時に型を指定する必要があります(またはコンテクスト)。静的呼び出しは、オブジェクトメソッド呼び出しが今日行うように、(動的)型階層を使用して呼び出しを解決できます。
これは簡単に実行でき(Javaを変更した場合:-O)、まったく不合理ではありませんが、いくつかの興味深い考慮事項があります。
主な考慮事項は、どの静的メソッド呼び出しがこれを実行するかを決定する必要があるということです。
現時点では、Javaにはこの言語の「癖」があり、obj.staticMethod()
呼び出しは呼び出しに置き換えられObjectClass.staticMethod()
ます(通常は警告が表示されます)。[注: ObjectClass
はのコンパイル時タイプですobj
。]これらは、実行時タイプの。を使用して、この方法でオーバーライドするのに適した候補ですobj
。
そうすると、メソッド本体が読みにくくなります。親クラスの静的呼び出しは、動的に「再ルーティング」される可能性があります。これを回避するには、クラス名を使用して静的メソッドを呼び出す必要があります。これにより、(現在のように)コンパイル時の型階層で呼び出しがより明確に解決されます。
静的メソッドを呼び出す他の方法はもっと注意が必要です。実行時型のをとって、this.staticMethod()
と同じ意味である必要があります。ただし、これにより、装飾なしで(明らかにローカルの)静的メソッドを呼び出す既存のプログラムで問題が発生する可能性があります(これはほぼ間違いなくと同等です)。obj.staticMethod()
this
this.method()
では、飾り気のない呼び出しはstaticMethod()
どうですか?今日と同じことを行い、ローカルクラスのコンテキストを使用して何をするかを決定することをお勧めします。そうしないと、大きな混乱が生じます。もちろん、それは、が非静的メソッドであるかどうか、そして静的メソッドであるかどうかmethod()
を意味します。これはもう1つの混乱の原因です。this.method()
method
ThisClass.method()
method
この動作を変更した場合(および静的呼び出しを動的に非ローカルにする可能性がある場合)、クラスのメソッドの修飾子としてfinal
、の意味を再検討する必要がprivate
あります。その場合、メソッドはオーバーライドされないため、コンパイル時に安全に解決でき、ローカル参照として読み取るのに「安全」であるという事実に全員が慣れなければなりません。protected
static
private static
public final
実際、私たちは間違っていました。
Javaではデフォルトで静的メソッドをオーバーライドできませんが、Javaのクラスクラスとメソッドクラスのドキュメントをよく見ると、次の回避策によって静的メソッドのオーバーライドをエミュレートする方法を見つけることができます。
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
class RegularEmployee {
private BigDecimal salary = BigDecimal.ONE;
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".02");
}
public BigDecimal calculateBonus() {
return salary.multiply(this.getBonusMultiplier());
}
public BigDecimal calculateOverridenBonus() {
try {
// System.out.println(this.getClass().getDeclaredMethod(
// "getBonusMultiplier").toString());
try {
return salary.multiply((BigDecimal) this.getClass()
.getDeclaredMethod("getBonusMultiplier").invoke(this));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
// ... presumably lots of other code ...
}
final class SpecialEmployee extends RegularEmployee {
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".03");
}
}
public class StaticTestCoolMain {
static public void main(String[] args) {
RegularEmployee Alan = new RegularEmployee();
System.out.println(Alan.calculateBonus());
System.out.println(Alan.calculateOverridenBonus());
SpecialEmployee Bob = new SpecialEmployee();
System.out.println(Bob.calculateBonus());
System.out.println(Bob.calculateOverridenBonus());
}
}
結果の出力:
0.02
0.02
0.02
0.03
私たちが達成しようとしていたこと:)
3番目の変数CarlをRegularEmployeeとして宣言し、それにSpecialEmployeeのインスタンスを割り当てたとしても、最初のケースではRegularEmployeeメソッドを呼び出し、2番目のケースではSpecialEmployeeメソッドを呼び出します。
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
出力コンソールを見てください。
0.02
0.03
;)
静的メソッドはJVMによってグローバルとして扱われ、オブジェクトインスタンスにバインドされることはありません。
(Smalltalkのような言語のように)クラスオブジェクトから静的メソッドを呼び出すことができれば、概念的には可能かもしれませんが、Javaの場合はそうではありません。
編集
静的メソッドをオーバーロードできます。それで問題ありません。ただし、クラスはファーストクラスのオブジェクトではないため、静的メソッドをオーバーライドすることはできません。リフレクションを使用して実行時にオブジェクトのクラスを取得できますが、取得するオブジェクトはクラス階層と並列ではありません。
class MyClass { ... }
class MySubClass extends MyClass { ... }
MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();
ob2 instanceof MyClass --> true
Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();
clazz2 instanceof clazz1 --> false
クラスを振り返ることはできますが、そこで止まります。を使用して静的メソッドを呼び出すのではなく、を使用して呼び出しclazz1.staticMethod()
ますMyClass.staticMethod()
。静的メソッドはオブジェクトにバインドされていないため、静的メソッドの概念this
もsuper
静的メソッドにもありません。静的メソッドはグローバル関数です。結果として、ポリモーフィズムの概念もありません。したがって、メソッドのオーバーライドは意味がありません。
しかし、これは、Smalltalkのように実行時にメソッドを呼び出すオブジェクトである場合に可能である可能性MyClass
があります(または、1つのコメントが示唆するようにJRubyかもしれませんが、JRubyについては何も知りません)。
そうそう...もう1つ。オブジェクトを介して静的メソッドを呼び出すことはできますobj1.staticMethod()
が、それは実際には構文糖衣構文でMyClass.staticMethod()
あり、避ける必要があります。通常、最新のIDEでは警告が発生します。なぜ彼らがこのショートカットを許可したのかわかりません。
メソッドのオーバーライドは、動的ディスパッチによって可能になります。つまり、オブジェクトの宣言されたタイプはその動作を決定するのではなく、実行時のタイプを決定します。
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
lassie
とは両方ともkermit
型のオブジェクトとして宣言されていますが、動的ディスパッチはコンパイル時ではなく実行時にのみメソッド呼び出しを実装にバインドするAnimal
ため、それらの動作(メソッド)は異なります。.speak()
.speak()
ここで、static
キーワードが意味をなし始めます。「静的」という言葉は「動的」の反意語です。したがって、静的メソッドをオーバーライドできない理由は、静的メンバーに動的ディスパッチがないためです。静的は文字通り「動的ではない」ことを意味するためです。それらが動的にディスパッチされた場合(したがってオーバーライドされる可能性がある場合)、static
キーワードはもはや意味をなさなくなります。
はい。実際には、Javaは静的メソッドのオーバーライドを許可します。理論的には、Javaで静的メソッドをオーバーライドすると、コンパイルと実行はスムーズになりますが、Javaの基本的なプロパティであるポリモーフィズムは失われます。コンパイルして実行することはできないことをどこでも読むことができます。あなたはあなたの答えを得るでしょう。たとえば、クラスAnimalと静的メソッドeat()があり、そのサブクラスでその静的メソッドをオーバーライドすると、Dogと呼ばれるようになります。次に、DogオブジェクトをAnimal Referenceに割り当て、Javaに従ってeat()を呼び出すと、Dogのeat()が呼び出されるはずですが、静的なオーバーライドでは、eat()が呼び出されます。
class Animal {
public static void eat() {
System.out.println("Animal Eating");
}
}
class Dog extends Animal{
public static void eat() {
System.out.println("Dog Eating");
}
}
class Test {
public static void main(String args[]) {
Animal obj= new Dog();//Dog object in animal
obj.eat(); //should call dog's eat but it didn't
}
}
Output Animal Eating
Javaのポリモーフィズムの原則によれば、出力はである必要がありますDog Eating
。
ただし、ポリモーフィズムをサポートするためにJavaは遅延バインディングを使用するため、結果は異なります。つまり、メソッドは実行時にのみ呼び出され、静的メソッドの場合は呼び出されません。静的メソッドでは、コンパイラは実行時ではなくコンパイル時にメソッドを呼び出すため、オブジェクトではなく参照に従ってメソッドを取得します。そのため、静的オーバーリングをサポートしていると言えますが、理論的にはサポートされていません。 't。
Java(および多くのOOP言語ですが、すべてを話すことはできません。静的なものがまったくないものもあります)では、すべてのメソッドに固定のシグニチャー(パラメーターとタイプ)があります。仮想メソッドでは、最初のパラメーターが暗黙指定されます。オブジェクト自体への参照であり、オブジェクト内から呼び出されると、コンパイラーは自動的にを追加しますthis
。
静的メソッドに違いはありません-それらはまだ固定された署名を持っています。ただし、メソッドstaticを宣言することにより、コンパイラーがそのシグニチャーの先頭に暗黙のオブジェクトパラメーターを含めてはならないことを明示的に示しました。したがって、これを呼び出す他のコードは、スタック上のオブジェクトへの参照を配置しようとしてはなりません。そうすると、パラメーターがスタック上の間違った場所(1つシフト)にあるため、メソッドの実行は機能しません。
この2つの違いのために; 仮想メソッドは常にコンテキストオブジェクト(つまりthis
)への参照を持っているため、オブジェクトのそのインスタンスに属するヒープ内のすべてを参照することができます。ただし、静的メソッドでは、参照が渡されないため、コンテキストが不明であるため、そのメソッドはオブジェクト変数およびメソッドにアクセスできません。
Javaが定義を変更して、静的または仮想のすべてのメソッドにオブジェクトコンテキストが渡されるようにする場合は、本質的に仮想メソッドのみが必要になります。
誰かがopへのコメントで尋ねたように-この機能が欲しい理由と目的は何ですか?
私はRubyをあまり知りません。これは、OPによって言及されたので、私はいくつかの調査を行いました。Rubyのクラスは本当に特別な種類のオブジェクトであり、(動的にでも)新しいメソッドを作成できることがわかります。クラスはRubyではフルクラスオブジェクトであり、Javaではありません。これは、Java(またはC#)を使用するときに受け入れる必要があるものです。これらは動的言語ではありませんが、C#はいくつかの形式の動的言語を追加しています。実際には、Rubyには、私が見つける限り「静的」メソッドはありません。その場合、これらはシングルトンクラスオブジェクトのメソッドです。次に、このシングルトンを新しいクラスでオーバーライドできます。前のクラスオブジェクトのメソッドは、新しいクラスで定義されたメソッドを呼び出します(正しいですか?)。したがって、元のクラスのコンテキストでメソッドを呼び出した場合でも、元の統計のみが実行されます。ただし、派生クラスのメソッドを呼び出すと、親クラスまたはサブクラスのいずれかからメソッドが呼び出されます。興味深いことに、私はその中にいくつかの価値を見ることができます。それは異なる思考パターンを取ります。
Javaで作業しているので、その方法に適応する必要があります。なぜ彼らはこれをしたのですか?まあ、おそらく利用可能な技術と理解に基づいて、当時のパフォーマンスを改善するためです。コンピュータ言語は絶えず進化しています。十分に戻って、OOPのようなものはありません。将来的には、他の新しいアイデアがあります。
編集:もう1つのコメント。違いがわかり、Java / C#開発者自身として、Rubyのような言語から来ている場合、Java開発者から得られる答えが混乱する理由を理解できます。Javastatic
メソッドはRubyメソッドと同じではありませんclass
。逆に、Ruby / Smalltalkのような言語で主に作業する開発者と同様に、Java開発者はこれを理解するのに苦労するでしょう。Javaも静的メソッドについて話す別の方法として「クラスメソッド」を使用しているという事実によって、これがどのように非常に混乱するかがわかりますが、この同じ用語はRubyでは異なって使用されています。JavaにはRubyスタイルのクラスメソッドがありません(申し訳ありません)。Rubyには、Cで見られるような、実際には古い手続き型関数であるJavaスタイルの静的メソッドがありません。
ちなみに、質問ありがとうございます!今日、クラスメソッド(Rubyスタイル)について何か新しいことを学びました。
オーバーライドは、ポリモーフィックな動作をサポートするためにインスタンスメンバー用に予約されています。静的クラスのメンバーは特定のインスタンスに属していません。代わりに、静的メンバーはクラスに属し、その結果、サブクラスは保護されたパブリックインスタンスメンバーのみを継承し、静的メンバーは継承しないため、オーバーライドはサポートされません。別のアプローチを評価するために、インターフェースおよび調査ファクトリおよび/または戦略設計パターンを定義することをお勧めします。
ええと...Javaでオーバーライドされたメソッドがどのように動作するかという観点から考えると、答えはノーです。ただし、静的メソッドをオーバーライドしようとしても、コンパイラエラーは発生しません。つまり、オーバーライドしようとしても、Javaはそれを止めません。ただし、非静的メソッドの場合と同じ効果は得られません。Javaでオーバーライドするということは、オブジェクトのコンパイル時タイプではなく、オブジェクトの実行時タイプに基づいて特定のメソッドが呼び出されることを意味します(オーバーライドされた静的メソッドの場合)。さて...彼らが奇妙に振る舞う理由の推測はありますか?これらはクラスメソッドであるため、コンパイル時のタイプ情報のみを使用して、コンパイル時に常にそれらへのアクセスが解決されます。
例:静的メソッドをオーバーライドしようとするとどうなるか見てみましょう:-
class SuperClass {
// ......
public static void staticMethod() {
System.out.println("SuperClass: inside staticMethod");
}
// ......
}
public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
System.out.println("SubClass: inside staticMethod");
}
// ......
public static void main(String[] args) {
// ......
SuperClass superClassWithSuperCons = new SuperClass();
SuperClass superClassWithSubCons = new SubClass();
SubClass subClassWithSubCons = new SubClass();
superClassWithSuperCons.staticMethod();
superClassWithSubCons.staticMethod();
subClassWithSubCons.staticMethod();
// ...
}
}
出力:-
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod
出力の2行目に注意してください。staticMethodがオーバーライドされていた場合、ランタイムタイプのオブジェクトで「SuperClass」ではなく「SubClass」として「staticMethod()」を呼び出しているため、この行は3行目と同じである必要があります。これにより、静的メソッドは常にコンパイル時のタイプ情報のみを使用して解決されることが確認されます。
私はジェイのコメントが好きで、2倍にしています(https://stackoverflow.com/a/2223803/1517187)。
これがJavaの悪い設計であることに同意します。
以前のコメントで見たように、他の多くの言語は静的メソッドのオーバーライドをサポートしています。ジェイも私のようにDelphiからJavaに来たように感じます。
Delphi(Object Pascal)は、Javaより前にOOPを実装した言語の1つであり、商用アプリケーション開発に使用された最初の言語の1つでした。
過去に商用GUI製品を作成する唯一の言語であったため、多くの人がその言語を経験したことは明らかです。そして-はい、Delphiでは静的メソッドをオーバーライドできます。実際、Delphiの静的メソッドは「クラスメソッド」と呼ばれますが、Delphiには、初期バインディングを持つメソッドである「Delphi静的メソッド」という異なる概念がありました。遅延バインディングを使用する必要があったメソッドをオーバーライドするには、「仮想」ディレクティブを宣言します。とても便利で直感的で、Javaでこれを期待していました。
一般に、静的メソッドの「オーバーライド」を許可することは意味がありません。実行時に呼び出すメソッドを決定する良い方法がないためです。Employeeを例にとると、RegularEmployee.getBonusMultiplier()を呼び出すと、どのメソッドが実行されることになりますか?
Javaの場合、オブジェクトインスタンスを介して呼び出される限り、静的メソッドを「オーバーライド」できる言語定義を想像することができます。ただし、これは通常のクラスメソッドを再実装するだけで、実際には何のメリットも追加せずに言語に冗長性を追加します。
オーバーライドすることで、オブジェクトタイプに応じてポリモーフィックな性質を作成できます。静的メソッドはオブジェクトとは関係ありません。したがって、Javaは静的メソッドのオーバーライドをサポートできません。
オーバーライドすることで、動的なポリモーフィズムを実現します。静的メソッドのオーバーライドと言うとき、使用しようとしている言葉は矛盾しています。
静的と言う-コンパイル時、オーバーライドは動的ポリモーフィズムに使用されます。どちらも本質的に反対であるため、一緒に使用することはできません。
動的なポリモーフィックな動作は、プログラマーがオブジェクトを使用してインスタンスメソッドにアクセスするときに発生します。JREは、使用しているオブジェクトの種類に基づいて、さまざまなクラスのさまざまなインスタンスメソッドをマップします。
静的メソッドのオーバーライドとは、コンパイル時にリンクされるクラス名を使用してアクセスする静的メソッドであるため、実行時にメソッドを静的メソッドにリンクするという概念はありません。したがって、静的メソッドを「オーバーライドする」という用語自体は意味を持ちません。
注:オブジェクトを使用してクラスメソッドにアクセスする場合でも、Javaコンパイラはそれを見つけるのに十分インテリジェントであり、静的リンクを実行します。
静的メソッドをオーバーライドするのはどのような効果がありますか。インスタンスを介して静的メソッドを呼び出すことはできません。
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
編集:言語設計の不幸な見落としにより、インスタンスを介して静的メソッドを呼び出すことができるようです。一般的に誰もそれをしません。私の悪い。
Javaでオーバーライドするということは、オブジェクトのコンパイル時タイプではなく、オブジェクトの実行時タイプに基づいて特定のメソッドが呼び出されることを意味します(オーバーライドされた静的メソッドの場合)。静的メソッドはクラスメソッドであるため、インスタンスメソッドではありません。静的メソッドの性質上、特定のクラスに属しているため、どの参照がどのオブジェクトまたはインスタンスを指しているかとは関係ありません。サブクラスで再宣言することはできますが、そのサブクラスは、親クラスの静的メソッドについて何も知りません。これは、前述したように、宣言されたクラスにのみ固有であるためです。オブジェクト参照を使用してそれらにアクセスすることは、Javaの設計者によって与えられた単なる追加の自由であり、詳細と例を制限する場合にのみ、その慣行を停止することを考えるべきではありません。 http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html
この質問の答えは簡単です。静的としてマークされたメソッドまたは変数はクラスのみに属します。したがって、静的メソッドはスーパークラスのみに属するため、サブクラスに継承できません。
簡単な解決策:シングルトンインスタンスを使用します。オーバーライドと継承が可能になります。
私のシステムには、渡されたクラスのインスタンスを返すSingletonsRegistryクラスがあります。インスタンスが見つからない場合は作成されます。
Haxe言語クラス:
package rflib.common.utils;
import haxe.ds.ObjectMap;
class SingletonsRegistry
{
public static var instances:Map<Class<Dynamic>, Dynamic>;
static function __init__()
{
StaticsInitializer.addCallback(SingletonsRegistry, function()
{
instances = null;
});
}
public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
{
if (instances == null) {
instances = untyped new ObjectMap<Dynamic, Dynamic>();
}
if (!instances.exists(cls))
{
if (args == null) args = [];
instances.set(cls, Type.createInstance(cls, args));
}
return instances.get(cls);
}
public static function validate(inst:Dynamic, cls:Class<Dynamic>)
{
if (instances == null) return;
var inst2 = instances[cls];
if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
}
}
ここに簡単な説明があります。静的メソッドはクラスに関連付けられ、インスタンスメソッドは特定のオブジェクトに関連付けられます。オーバーライドを使用すると、特定のオブジェクトに関連付けられているオーバーライドされたメソッドのさまざまな実装を呼び出すことができます。したがって、オブジェクトに関連付けられていない静的メソッドをオーバーライドするのは直感に反しますが、そもそもクラス自体に関連付けられています。したがって、静的メソッドは、それを呼び出しているオブジェクトに基づいてオーバーライドすることはできません。静的メソッドは、それが作成されたクラスに常に関連付けられます。
静的メソッド、変数、ブロック、またはネストされたクラスは、オブジェクトではなくクラス全体に属します。
Javaのメソッドは、オブジェクト/クラスの動作を公開するために使用されます。ここで、メソッドは静的であるため(つまり、静的メソッドはクラスの動作のみを表すために使用されます)、クラス全体の動作を変更/オーバーライドすると、オブジェクト指向プログラミングの基本的な柱の1つである高凝集度の現象に違反します。 。(コンストラクターはJavaの特別な種類のメソッドであることを忘れないでください。)
高い凝集度-1つのクラスには1つの役割のみが必要です。例:車のクラスは、自転車、トラック、飛行機などではなく、車のオブジェクトのみを生成する必要があります。ただし、車のクラスには、それ自体にのみ属するいくつかの機能(動作)がある場合があります。
したがって、Javaプログラミング言語を設計している間。言語設計者は、メソッドを本質的に静的にすることによってのみ、開発者がクラスの一部の動作をそれ自体に保持できるようにすることを考えました。
以下のピースコードは静的メソッドをオーバーライドしようとしますが、コンパイルエラーは発生しません。
public class Vehicle {
static int VIN;
public static int getVehileNumber() {
return VIN;
}}
class Car extends Vehicle {
static int carNumber;
public static int getVehileNumber() {
return carNumber;
}}
これは、ここではメソッドをオーバーライドするのではなく、再宣言するだけだからです。Javaでは、メソッドの再宣言(静的/非静的)が可能です。
CarクラスのgetVehileNumber()メソッドからstaticキーワードを削除すると、コンパイルエラーが発生します。これは、Vehicleクラスのみに属するstaticメソッドの機能を変更しようとしているためです。
また、getVehileNumber()がfinalとして宣言されている場合、コードはコンパイルされません。これは、finalキーワードにより、プログラマーがメソッドを再宣言できないためです。
public static final int getVehileNumber() {
return VIN; }
全体として、これは静的メソッドをどこで使用するかについてはソフトウェア設計者次第です。私は個人的に、クラスのインスタンスを作成せずに静的メソッドを使用していくつかのアクションを実行することを好みます。第二に、クラスの振る舞いを外の世界から隠すこと。
上記の回答を見ると、静的メソッドをオーバーライドできないことは誰もが知っていますが、サブクラスから静的メソッドにアクセスするという概念について誤解してはなりません。
この静的メソッドがサブクラスで定義された新しい静的メソッドによって隠されていない場合は、サブクラス参照を使用してスーパークラスの静的メソッドにアクセスできます。
たとえば、以下のコードを参照してください。-
public class StaticMethodsHiding {
public static void main(String[] args) {
SubClass.hello();
}
}
class SuperClass {
static void hello(){
System.out.println("SuperClass saying Hello");
}
}
class SubClass extends SuperClass {
// static void hello() {
// System.out.println("SubClass Hello");
// }
}
出力:-
SuperClass saying Hello
サブクラスでの静的メソッドの非表示の詳細については、 Java oracleドキュメントを参照し、サブクラスで実行できることを検索してください。
ありがとう
次のコードは、それが可能であることを示しています。
class OverridenStaticMeth {
static void printValue() {
System.out.println("Overriden Meth");
}
}
public class OverrideStaticMeth extends OverridenStaticMeth {
static void printValue() {
System.out.println("Overriding Meth");
}
public static void main(String[] args) {
OverridenStaticMeth osm = new OverrideStaticMeth();
osm.printValue();
System.out.println("now, from main");
printValue();
}
}