Javaが静的メソッドのオーバーライドを許可しないのはなぜですか?


534

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

可能であれば、例を使用してください。


3
ほとんどのOOP言語はこれを許可していません。
jmucchiello 2010

7
@jmucchiello:私の答えを見てください。私はあなたと同じように考えていましたが、Ruby / Smalltalkの「クラス」メソッドについて学んだので、これを行う他の真のOOP言語があります。
Kevin Brock

5
@jmucchielloほとんどのOOP言語は本当のOOP言語ではない(私はSmalltalkだと思う)
mathk


1
Javaがコンパイル時に静的メソッドの呼び出しを解決することが原因である可能性があります。だから、あなたが書かれている場合でもParent p = new Child()、その後、p.childOverriddenStaticMethod()コンパイラはそれを解決しますParent.childOverriddenStaticMethod()参照型を調べることで。
Manoj 2016年

回答:


494

オーバーライドは、クラスのインスタンスがあるかどうかに依存します。ポリモーフィズムのポイントは、クラスをサブクラス化でき、それらのサブクラスを実装するオブジェクトは、スーパークラスで定義された(およびサブクラスでオーバーライドされた)同じメソッドに対して異なる動作をすることです。静的メソッドはクラスのインスタンスに関連付けられていないため、この概念は適用されません。

これに影響を与えるJavaの設計を推進する2つの考慮事項がありました。1つはパフォーマンスに関する懸念でした。Smalltalkが遅すぎる(ガベージコレクションとポリモーフィックコールがその一部である)との批判が多く、Javaの作成者はそれを回避しようと決心しました。もう1つは、Javaの対象読者がC ++開発者であるという決定でした。静的メソッドを機能させる方法は、C ++プログラマーにとってなじみ深いという利点があり、また、どのメソッドを呼び出すかを実行時まで把握するためにランタイムまで待つ必要がないため、非常に高速でした。


18
...しかし、Javaでは「正しい」だけです。たとえば、Scalaの「静的クラス」に相当するもの(と呼ばれますobjects)では、メソッドのオーバーロードが可能です。

32
Objective-Cでは、クラスメソッドをオーバーライドすることもできます
Richard

11
コンパイル時の型の階層と実行時の型の階層があります。静的メソッド呼び出しが存在する状況で、ランタイム型階層を利用できない理由を尋ねるのは完全に理にかなっています。Javaでは、これはオブジェクト(obj.staticMethod())から静的メソッドを呼び出すときに発生します。これは許可されており、コンパイル時の型を使用します。静的呼び出しがクラスの非静的メソッド内にある場合、「現在の」オブジェクトはクラスの派生型である可能性がありますが、派生型で定義された静的メソッドは考慮されません(これらはランタイム型にあります)階層)。
Steve Powell

18
私はそれが明らかにされている必要があります:ないというのは本当概念が適用されていません
Steve Powell

13
この答えは正しいですが、OPの、したがってここでは私自身や他の人々の期待に応えるためには、本来あるべき姿ではなく、より正確にいかにあるべきかというよりも、「現状」に近いものです。「それがそうである」以外に、静的メソッドのオーバーライドを禁止する具体的な理由はありません。個人的には欠点だと思います。
RichieHH 2014年

186

個人的には、これは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が静的を処理する方法に適合しないため、これが「悪い設計」であるということを意味する場合、私の返事は「まあ、もちろんです」です。元の投稿で述べたように、機能しません。しかし、これが機能した言語、つまり静的関数が仮想関数のようにオーバーライドされる可能性のある言語に根本的に何か問題があるという意味で設計が悪いことを意味する場合、これはどういうわけか曖昧さをもたらすか、またはそれを不可能にするでしょう効率的に実装するなどして、「なぜ?コンセプトの何が悪いの?」

私が挙げる例は、やりたいことは非常に自然なことだと思います。インスタンスデータに依存しない関数を持つクラスがあり、インスタンスから独立して呼び出したい場合や、インスタンスメソッド内から呼び出したい場合があります。なぜこれはうまくいかないのですか?私はこの状況に何年にもわたって何度も遭遇しました。実際には、関数を仮想化し、ダミーのインスタンスを使用して仮想メソッドに呼び出しを渡す静的メソッドを唯一の目的とする静的メソッドを作成することで回避します。それはそこに行くための非常に回り道のようです。


11
@Bemrose:でもそれが私のポイントです:なぜ私はそれを許されるべきではないのですか 「静的」が何をすべきかという私の直感的な概念はおそらくあなたのものとは異なりますが、基本的には静的はインスタンスデータを使用しないため静的である可能性があるメソッドであり、必要に応じて静的である必要がありますインスタンスとは関係なくそれを呼び出す。staticは明らかにクラスに関連付けられています。Integer.valueOfはIntegersに関連付けられ、Double.valueOfはDoublesに関連付けられることを期待しています。
ジェイ

9
@ewernli&Bemrose:はい、そうです。私はそれについて議論していません。私の例のコードは機能しないので、もちろんそれを記述しようとはしません。私の質問は、なぜそうなのかということです。(これが、私たちが通信していない会話の1つに変わるのではないかと心配しています。「すみません、セールスマンさん、これらの1つを赤で入手できますか?」「いいえ、5ドルかかります。」「はい、コストがかかります。 5ドルですが、赤いものを手に入れられますか?」「サー、5ドルかかると言ったところです」「わかりました、価格はわかっていますが、色について尋ねていました」「すでに価格を教えました!」など)
ジェイ

6
最終的にこのコードは混乱を招くと思います。インスタンスがパラメーターとして渡されるかどうかを検討してください。次に、ランタイムインスタンスがどの静的メソッドが呼び出されるかを指示する必要があると述べています。これにより、基本的に、個別の階層全体が既存のインスタンスの階層と並列になります。では、サブクラスが同じメソッドシグネチャを非静的として定義するとどうなるでしょうか。ルールは物事をかなり複雑にするだろうと思います。Javaが回避しようとしているのは、まさにこの種の言語の複雑化です。
Yishai

6
@Yishai:RE "実行時インスタンスは、どの静的メソッドが呼び出されるかを指示します":まさに。なぜ仮想でできるのに静的で何もできないのかわかりません。「個別の階層」:私はそれを同じ階層の一部にします。静的が同じ階層に含まれていないのはなぜですか?「サブクラスが同じシグネチャを非静的に定義している」:たとえば、サブクラスに同じシグネチャであるが戻り値の型が異なる関数をオーバーライドさせること、またはすべての例外をスローしないことは違法であると同様に、違法であると思います親が投げる、またはより狭いスコープを持つ。
ジェイ

28
ジェイはポイントを持っていると思います-静力学をオーバーライドできないことに気付いたときも驚きました。一部には、A withメソッドがsomeStatic()あり、BがAを拡張している場合、AのメソッドにB.someMethod() バインドします。その後someStatic()Bに追加した場合、呼び出し元のコードA.someStatic()を再コンパイルするまで呼び出し元のコードが引き続き呼び出されます。また、リンクではなくコンパイル時にバインドするため、ランタイムタイプではなく、bInstance bInstance.someStatic()宣言されたタイプを使用していることに驚きましたA bInstance; ... bInstance.someStatic()。B.someStatic()が存在する場合、A.someStatic()を呼び出します。
Lawrence Dol

42

簡単に言えば、それは完全に可能ですが、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()this.method()if methodが非静的メソッドであり、かつThisClass.method()if methodが静的メソッドだったということを意味します。これも混乱の原因です。

その他の考慮事項

この動作を変更した場合(および静的呼び出しを動的に非ローカルにする可能性がある場合)は、クラスのメソッドの修飾子としてfinalprivateおよびクラスのメソッドのprotected修飾子として再検討したいと思うでしょうstatic。メソッドprivate staticpublic finalメソッドがオーバーライドされないため、コンパイル時に安全に解決でき、ローカル参照として「安全」に読み取ることができることに慣れる必要があります。


「そうすると、メソッド本体が読みにくくなります。親クラスの静的呼び出しは、動的に「再ルーティング」される可能性があります。」確かに、これはまさに今、通常の非静的関数呼び出しで起こることです。これは、通常の仮想関数の肯定的な機能として日常的に宣伝されており、問題ではありません。
ジェイ

25

実際、私たちは間違っていました。
Javaではデフォルトで静的メソッドをオーバーライドすることはできませんが、JavaのClassおよびMethodクラスのドキュメントを徹底的に調べれば、次の回避策で静的メソッドのオーバーライドをエミュレートする方法を見つけることができます。

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

;)


9
はい、反射はほとんどできる唯一のことです-しかし、問題はこれだけではありません-しかし、ここでそれを使用するのに役立ちます
Mr_and_Mrs_D

1
この答えは、これまでにすべてのJavaトピックで見た最大のハックです。まだそれを読むのは面白かった:)
Andrejs

19

静的メソッドは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静的メソッドでは。静的メソッドはグローバル関数です。結果として、ポリモーフィズムの概念もないため、メソッドのオーバーライドは意味がありません。

しかしMyClass、Smalltalkのように、メソッドを呼び出す実行時のオブジェクトである場合(または、JRubyのコメントの1つが示唆しているが、JRubyについては何も知らない場合)は、この可能性があります。

そうそう...もう一つ。オブジェクトを介して静的メソッドを呼び出すことはできますobj1.staticMethod()が、これは本当に構文上の問題でMyClass.staticMethod()あり、避ける必要があります。最近のIDEでは通常、警告が表示されます。彼らがこのショートカットを許可した理由を私は知りません。


5
Rubyのような多くの現代の言語でさえクラスメソッドを持ち、それらをオーバーライドすることができます。
Chandra Sekar

3
クラスはJavaのオブジェクトとして存在します。「クラス」クラスをご覧ください。myObject.getClass()と言うと、適切なクラスオブジェクトのインスタンスが返されます。
ジェイ

5
クラス自体ではなく、クラスの「説明」のみを取得します。しかし、違いは微妙です。
ewernli 2010

クラスはまだありますが、VM(クラスローダーの近く)で非表示になっているため、ユーザーはほとんどアクセスできません。
mathk

使用するにはclazz2 instanceof clazz1適切にあなたの代わりに使用することができclass2.isAssignableFrom(clazz1)、私はあなたの例ではtrueを返すと信じています、。
Simon Forsberg 2013年

14

メソッドのオーバーライドは、動的ディスパッチングによって可能になります。つまり、オブジェクトの宣言されたタイプによって動作が決定されるのではなく、ランタイムタイプが決定されます。

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

lassieとの両方kermitがtypeのオブジェクトとして宣言されている場合でも、動的ディスパッチングは、コンパイル時ではなく実行時にメソッド呼び出しを実装にのみバインドするためAnimal、それらの動作(メソッド.speak())は異なります。.speak()

ここで、staticキーワードが意味を持ち始めるところです。「静的」という単語は「動的」の反意語です。したがって、静的メソッドをオーバーライドできないのは、静的メンバーに動的ディスパッチがないためです。静的とは、文字通り「動的ではない」という意味です。それらが動的にディスパッチされた場合(したがってオーバーライドされる可能性がある場合)、staticキーワードはもはや意味をなさなくなります。


11

はい。実際には、Javaは静的メソッドをオーバーライドできます。理論的には、Javaで静的メソッドをオーバーライドすると、コンパイルと実行はスムーズに行われますが、Javaの基本的なプロパティであるポリモーフィズムは失われます。自分でコンパイルして実行することは不可能であることを、どこでも読むことができます。あなたはあなたの答えを得るでしょう。たとえば、Animalクラスと静的メソッドeat()があり、その静的メソッドをサブクラスでオーバーライドすると、Dogと呼ばれます。次に、Dogオブジェクトを動物参照に割り当て、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はレイトバインディングを使用するため、結果は異なりました。つまり、メソッドは実行時にのみ呼び出され、静的メソッドの場合には呼び出されません。静的メソッドでは、コンパイラは実行時ではなくコンパイル時にメソッドを呼び出すため、参照に従ってメソッドを取得します。オブジェクトではなく参照を含むメソッドを取得します。そのため、実際には静的オーバーリングをサポートしていますが、理論的にはそうですない


3
オブジェクトから静的メソッドを呼び出すことは悪い習慣です。
Dmitry Zagorulkin

6

オーバーライドは、インスタンスのメンバーが多態的な動作をサポートするために予約されています。静的クラスのメンバーは特定のインスタンスに属していません。代わりに、静的メンバーはクラスに属し、サブクラスは保護されたインスタンスメンバーとパブリックインスタンスメンバーのみを継承し、静的メンバーは継承しないため、オーバーライドはサポートされません。別のアプローチを評価するために、インターフェースおよび研究工場や戦略設計パターンを定義することができます。


1
これをすでにカバーしている他の回答を読んでおらず、これらは概念レベルでの静的のオーバーライドを無視するのに十分な理由ではないことを明らかにしましたか?私たちはそれが機能しないことを知っています。静的メソッドのオーバーライドを望むことは完全に「きれいで、確かにそれは他の多くの言語で可能です。
RichieHH

リチャード、この質問に4年前に回答したとき、これらの回答のほとんどが投稿されていなかったとしばらく考えましょう。私が注意深く読んでいないと断言する必要はありません。さらに、Javaに関するオーバーライドについてのみ説明していることはお読みになりませんでした。他の言語で何ができるか気にかける人。それは無関係です。どこか別の場所でトロールしてください。あなたのコメントは、このスレッドに価値のあるものを追加しません。
アテネホロウェイ

6

Java(および多くのOOP言語ですが、すべてを話すことはできません。一部はまったく静的ではありません)では、すべてのメソッドに固定のシグニチャー(パラメーターとタイプ)があります。仮想メソッドでは、最初のパラメーターが暗黙的に含まれています。オブジェクト自体への参照であり、オブジェクト内から呼び出されると、コンパイラーは自動的にを追加しますthis

静的メソッドに違いはありません-それらはまだ固定された署名を持っています。ただし、静的メソッドを宣言することにより、コンパイラーは暗黙のオブジェクトパラメーターをそのシグニチャーの先頭に含めてはならないことを明示的に述べました。したがって、これを呼び出す他のコードでは、オブジェクトへの参照をスタックに配置してはなりません。それを行った場合、パラメータがスタック上の間違った場所(1つシフト)にあるため、メソッドの実行は機能しません。

この2つの違いにより、仮想メソッドは常にコンテキストオブジェクト(つまりthis)への参照を持っているので、オブジェクトのそのインスタンスに属するヒープ内の任意のものを参照できます。ただし、静的メソッドでは、渡される参照がないため、コンテキストが不明であるため、そのメソッドはオブジェクト変数やメソッドにアクセスできません。

Javaが定義を変更してオブジェクトコンテキストが静的または仮想のすべてのメソッドに渡されるようにしたい場合は、基本的に仮想メソッドのみを使用します。

誰かがオペレーションへのコメントで尋ねたように-この機能が必要な理由と目的は何ですか?

Rubyについてはあまり知りません。これはOPで言及されていたので、調査を行いました。Rubyのクラスは本当に特別な種類のオブジェクトであり、(動的であっても)新しいメソッドを作成できることがわかります。クラスはRubyでは完全なクラスオブジェクトであり、Javaではありません。これは、Java(またはC#)で作業するときに受け入れる必要があるものです。これらは動的言語ではありませんが、C#はいくつかの形式の動的言語を追加しています。実際には、Rubyには私が見つけられる限り「静的」メソッドはありません。その場合、これらはシングルトンクラスオブジェクトのメソッドです。次に、このシングルトンを新しいクラスでオーバーライドできます。前のクラスオブジェクトのメソッドは、新しいクラスで定義されたメソッドを呼び出します(正しい?)。したがって、元のクラスのコンテキストでメソッドを呼び出した場合でも、元の静的オブジェクトのみが実行されます。ただし、派生クラスのメソッドを呼び出すと、親クラスまたはサブクラスからメソッドが呼び出されます。おもしろいですし、それに価値があると思います。それは別の思考パターンを取ります。

Javaで作業しているので、その方法で調整する必要があります。なぜ彼らはこれをしたのですか?おそらく、利用可能なテクノロジーと理解に基づいて、当時のパフォーマンスを改善するためでしょう。コンピュータ言語は常に進化しています。十分に前に戻ると、OOPのようなものはありません。将来的には、他にも新しいアイデアがあります。

編集:もう1つのコメント。違いがわかり、Java / C#開発者自身も、Rubyのような言語を使用している場合にJava開発者からの回答が混乱する理由を理解できます。Java staticメソッドはRuby classメソッドと同じではありません。Java開発者はこれを理解するのに苦労します。逆に、Ruby / Smalltalkのような言語で主に作業する人は逆です。Javaも静的メソッドについて語る別の方法として「クラスメソッド」を使用しているが、Rubyではこの同じ用語が異なる方法で使用されているという事実によって、これがどのように非常に混乱するかもわかります。JavaにはRubyスタイルのクラスメソッドがありません(申し訳ありません)。RubyにはJavaスタイルの静的メソッドはありません。これは、Cで見られるような、古いスタイルの関数にすぎません。

ちなみに、質問ありがとうございます!今日、クラスメソッド(Rubyスタイル)について新しいことを学びました。


1
もちろん、Javaが "Class"オブジェクトを隠しパラメーターとして静的メソッドに渡せなかった理由はありません。それを行うように設計されていません。
jmucchiello 2010年

6

まあ... Javaでオーバーライドされたメソッドがどのように動作するかという観点から考えると、答えはNOです。ただし、静的メソッドをオーバーライドしようとしても、コンパイラエラーは発生しません。つまり、オーバーライドしようとしても、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行目と同じである必要があります。これにより、静的メソッドは常にコンパイル時の型情報のみを使用して解決されることが確認されます。


5

通常、静的メソッドの「オーバーライド」を許可しても、実行時に呼び出すメソッドを決定する適切な方法がないため、意味がありません。Employeeの例で、RegularEmployee.getBonusMultiplier()を呼び出すと、どのメソッドが実行されることになっていますか?

Javaの場合、オブジェクトのインスタンスを通じて呼び出される限り、静的メソッドを「オーバーライド」できる言語定義を想像できます。ただし、これでできることは、通常のクラスメソッドを再実装して、言語に冗長性を追加するだけで、実際には何の利点もありません。


2
直感的には、仮想関数のように機能するはずです。BがAを拡張し、AとBの両方にdoStuffという仮想関数がある場合、コンパイラーは、AのインスタンスがA.doStuffを使用し、BのインスタンスがB.doStuffを使用する必要があることを認識します。静的関数でも同じことができないのはなぜですか?結局のところ、コンパイラーは、各オブジェクトがどのクラスのインスタンスであるかを認識しています。
2010

Erm ... Jay、静的メソッドをインスタンスで呼び出す必要はありません(通常は呼び出されません)
meriton

2
@meriton、でもそれはもっと簡単ですよね?クラス名を使用して静的メソッドが呼び出された場合は、クラスに適したメソッドを使用します。
CPerkins 2010

しかし、あなたにとって何が優先されているのか。A.doStuff()を呼び出す場合は、「B extends A」でオーバーライドされたバージョンまたは「C extends A」でオーバーライドされたバージョンを使用する必要があります。また、CまたはBを使用している場合は、とにかくこれらのバージョンを呼び出しています...上書きする必要はありません。
PSpeed

@meriton:静的メソッドは一般にインスタンスでは呼び出されないのは事実ですが、そのような呼び出しはJavaの現在の設計では何も役に立たないためだと思います!別のデザインの方が良いアイデアだったかもしれないと私は提案しています。ところで、非常に現実的な意味で、静的関数はインスタンスを使用して非常に日常的に呼び出されます。つまり、仮想関数内から静的関数を呼び出す場合です。次に、暗黙的にthis.function()、つまり現在のインスタンスを取得します。
ジェイ

5

オーバーライドすることで、オブジェクトタイプに応じて多態性を作成できます。静的メソッドはオブジェクトと関係がありません。したがって、Javaは静的メソッドのオーバーライドをサポートできません。


5

私はジェイのコメントが好きで倍増しますhttps://stackoverflow.com/a/2223803/1517187)。
これはJavaの悪い設計であることに同意します。
他の多くの言語は、以前のコメントで見たように、静的メソッドのオーバーライドをサポートしています。ジェイも私と同じようにDelphiからJavaにやってきたと感じています。
Delphi(Object Pascal)は、OOPを実装した最初の言語でした。
過去には商用GUI製品を作成するための唯一の言語だったため、多くの人々がその言語の経験を持っていることは明らかです。そして-はい、Delphiで静的メソッドをオーバーライドできます。実際には、Delphiの静的メソッドは「クラスメソッド」と呼ばれていますが、Delphiには「Delphi静的メソッド」という異なる概念があり、アーリーバインディングが使用されています。レイトバインディングを使用する必要があったメソッドをオーバーライドするには、「仮想」ディレクティブを宣言します。とても便利で直感的で、Javaでこれを期待していました。


3

静的メソッドをオーバーライドすることで何ができるでしょうか。インスタンスを介して静的メソッドを呼び出すことはできません。

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

編集:残念ながら言語設計の見落としにより、インスタンスを通じて静的メソッドを呼び出すことができるようです。一般的には誰もそれをしません。私の悪い。


8
「インスタンスを介して静的メソッドを呼び出すことはできません」実際、Javaの癖の1つは、非常に悪い考えですが、インスタンスを介して静的メソッドを呼び出すことができるということです。
Powerlord、2010

1
実際、Javaではインスタンスを介して静的メンバーにアクセスできます。Javaの静的変数を
Richard JP Le Guen 2012

ただし、適切な最新のIDEでは警告を生成しますが、少なくともOracleが下位互換性を維持しながら警告を受け取ることができます。
ギンビー、2013年

1
インスタンスを介して静的メソッドを呼び出すことができることには、概念的に何も問題はありません。これは、動揺のための動揺です。Dateインスタンスのようなものが独自の静的メソッドを呼び出さず、呼び出し側のインターフェイスを介してインスタンスデータを関数に渡す必要があるのはなぜですか?
RichieHH 2014年

@RichieHHそれは彼らが不快に思っていることではありません。問題は、宣言された型の変数であるのvariable.staticMethod()代わりにを呼び出すことが許可される理由です。私はそれが悪い言語設計であることに同意します。Class.staticMethod()variableClass
フィッシュ

3

Javaでのオーバーライドとは、特定のメソッドがオブジェクトのコンパイル時の型ではなく、オブジェクトの実行時の型に基づいて呼び出されることを意味します(オーバーライドされた静的メソッドの場合)。静的メソッドはクラスメソッドであるため、インスタンスメソッドではないため、静的メソッドの性質上、特定のクラスに属しているため、どの参照がどのオブジェクトまたはインスタンスを指しているかとは関係ありません。サブクラスで再宣言することはできますが、そのサブクラスは、親クラスの静的メソッドについては何も知りません。これは、すでに述べたように、宣言されているクラスにのみ固有であるためです。オブジェクト参照を使用してそれらにアクセスすることは、Javaの設計者によって与えられた追加の自由であり、詳細と例を制限する場合にのみ、そのプラクティスを停止することは考えるべきではありません。 http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html


3

オーバーライドすることで、動的なポリモーフィズムを実現します。静的メソッドをオーバーライドすると言うと、使用しようとしている単語は矛盾しています。

静的は言う-コンパイル時、オーバーライドは動的なポリモーフィズムに使用されます。どちらも性質が逆なので、一緒に使用することはできません。

プログラマーがオブジェクトを使用してインスタンスメソッドにアクセスすると、動的なポリモーフィックな動作が発生します。JREは、使用しているオブジェクトの種類に基づいて、さまざまなクラスのさまざまなインスタンスメソッドをマップします。

静的メソッドをオーバーライドすると言うと、静的メソッドは、コンパイル時にリンクされるクラス名を使用してアクセスするため、実行時にメソッドを静的メソッドにリンクするという概念はありません。したがって、「オーバーライドする」静的メソッド自体は意味を持ちません。

注:オブジェクトを使用してクラスメソッドにアクセスする場合でも、Javaコンパイラはそれを見つけるのに十分なほどインテリジェントであり、静的リンクを行います。


これは、実行時に静的メソッドが実際に包含クラスのインスタンスから呼び出される多くのインスタンスには当てはまりません。したがって、呼び出す関数のインスタンスを決定することは完全に可能です。
RichieHH 2014年

staticはコンパイル時間を意味せず、staticは特定のオブジェクトではなくクラスにバインドされることを意味します。クラスファクトリの作成と同じくらい意味がありますBox.createBoxが、static はに比べて意味がありBoxFactory.createBox、例外をスローせずにエラーチェックの構築が必要な場合は避けられないパターンです(コンストラクタは失敗せず、プロセス/スローのみを終了できます)例外)一方、静的メソッドは失敗時にnullを返すか、hastebin.com / codajahati.javaのようなものを書き込むために成功/エラーコールバックを受け入れることさえできます。
ドミトリー

2

この質問の答えは簡単です。静的としてマークされたメソッドまたは変数はクラスにのみ属します。そのため、静的メソッドはスーパークラスにのみ属しているため、サブクラスで継承できません。


1
こんにちはG4uKu3_Gaurav。貢献を決定していただきありがとうございます。ただし、通常はこれよりも長く詳細な回答を期待しています。
DJクレイワース2015年

@DJClayworthあなたは詳細な回答のために、このリンクに従ってください geeksforgeeks.org/...
g1ji

リンクをありがとう。実際、私はこのサイトの初心者に役立つように、また、慣れていない人のためにサイトがどのように機能するかを説明するためにここにいます。質問への回答が必要だったからではありません。
DJClayworth、2015年

1

簡単な解決策:シングルトンインスタンスを使用します。オーバーライドと継承が可能になります。

私のシステムでは、渡されたClassのインスタンスを返す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!";
  }

}

かっこいいです
。Haxe

1
これは、クラス自体の静的メソッドとしてJavaに実装する方がはるかに優れています。Singleton.get()。レジストリは単なる定型的なオーバーヘッドであり、クラスのGCを排除します。
ローレンスドル

あなたが正しい、それは古典的なソリューションです。レジストリを選択した理由を正確には覚えていません。おそらく、この結果につながった思考の枠組みがあったのでしょう。
Raivo Fishmeister 2014

1

静的メソッド、変数、ブロック、またはネストされたクラス、オブジェクトではなくクラス全体に属します。

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クラスにのみ属する静的メソッドの機能を変更しようとしているためです。

また、getVehileNumber()がfinalとして宣言されている場合、コードはコンパイルされません。final キーワードは、プログラマがメソッドを再宣言することを制限するためです。

public static final int getVehileNumber() {
return VIN;     }

全体として、静的メソッドをどこで使用するかはソフトウェア設計者次第です。個人的には、静的メソッドを使用して、クラスのインスタンスを作成せずにいくつかのアクションを実行することを好みます。第二に、クラスの振る舞いを外の世界から隠すこと。


1

ここに簡単な説明があります。静的メソッドはクラスに関連付けられ、インスタンスメソッドは特定のオブジェクトに関連付けられます。オーバーライドにより、特定のオブジェクトに関連付けられたオーバーライドされたメソッドのさまざまな実装を呼び出すことができます。したがって、オブジェクトに関連付けられていない静的メソッドをオーバーライドするのは直観に反しますが、そもそもクラス自体はオーバーライドされます。そのため、静的メソッドは、それを呼び出しているオブジェクトに基づいてオーバーライドすることはできません。静的メソッドは、それが作成されたクラスに常に関連付けられます。


public abstract IBox createBox();内部IBoxインターフェースを使用することは、どのように直観に反していますか?BoxはIBoxを実装してcreateBoxをオーバーライドし、オブジェクトの作成結果を有効なIBoxにできます。それ以外の場合はnullを返します。コンストラクタは「null」を返すことができないため、(1)EVERYWHERE(今行っていること)の例外を使用するか、(2)先ほど言ったことを行うが、初心者やエキスパートには意味をなさない方法でファクトリクラスを作成する必要がありますJavaの(私たちも今やっている)。静的な実装されていないメソッドはこれを解決します。
ドミトリー

-1

上記の回答を見ると、静的メソッドをオーバーライドできないことは誰もが知っていますが、サブクラスから静的メソッドにアクセスする概念について誤解しないでください。

この静的メソッドがサブクラスで定義された新しい静的メソッドによって隠されていない場合、サブクラス参照を使用してスーパークラスの静的メソッドにアクセスできます。

例については、以下のコードを参照してください:

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ドキュメントを参照し、サブクラスで実行できることを検索してください

ありがとう


-3

次のコードは、それが可能であることを示しています。

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();

}   

} 

1
いいえ、ありません。の静的に宣言された型はでosmはありOverridenStaticMethませんOverrideStaticMeth
ローレンスドル2014

2
また、<big grin>のプログラミング中にMethをあまり使用しないようにします。
ローレンスドル2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.