質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

java:オーバーライドの際、staticメソッドに変更してはいけない理由について

解決済

回答 6

投稿

  • 評価
  • クリップ 3
  • VIEW 2,098

score 18

親クラスを継承した子クラスで、親クラスのインスタンスメソッドをオーバライドする際、いくつかのルールがあると思うのですが、その中で「インスタンスメソッドをstaticメソッドに変更してはいけない」というルールがどうしても腑に落ちません。

まず、正しいオーバーライドの構文を書きます。
下記ソースコードでは、SuperClassクラスを継承したSubClassクラスがSuperClassクラスで宣言しているmethodメソッドをオーバーライドしています。
そして、mainメソッドでSubClassクラスのインスタンスを生成し、インスタンスのアドレス値をSuperClassクラス型に暗黙変換し、sup変数に代入しています。
そして、SuperClass型のインスタンスのアドレス値が指すインスタンスのmethodメソッドを呼び出すと、オーバーライドのルールより、子クラスのメソッドが呼び出されるため、出力結果としては、「子クラス」と出ます。

public class Sample{
    public static void main(String[] args){
        SuperClass sup=new SubClass();
        sup.method();
    }
}
class SuperClass{
    public void method(){
        System.out.println("親クラス");
    }
}
class SubClass extends SuperClass{
    public  void method(){
        System.out.println("子クラス");
    }
}

ここからが疑問なのですが、
下記のようなソースコードを書くとメソッドのオーバーライドをしてる部分でコンパイルエラーとなります。理由としてはオーバーライドするメソッドのstatic修飾子を変更しているからです。それは分かるのですが、なぜ、staticなメソッドに変更してはいけないのでしょうか?
確かにインスタンスメソッドをstaticメソッドに変更することに違和感があると直感的には分かるのですが、いまいちピンときません。
また、今回のメソッドに関しては非staticメンバ変数を参照していないので、インスタンスメソッドでも非staticメソッドでも、機能面に異常はないように思えます。
ここの部分を詳しく教えていただける方がいましたら、よろしくお願いします。

public class Sample{
    public static void main(String[] args){
        SuperClass sup=new SubClass();
        sup.method();
    }
}
class SuperClass{
    public void method(){
        System.out.println("親クラス");
    }
}
class SubClass extends SuperClass{
    public static void method(){
        System.out.println("子クラス");
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+2

どちらもメソッドであると言うだけで、全く違う物だからとしか言いようが無いです。

変更したいという気持ちが出てくるとしたら、
・クラスの設計が間違っていた
・そもそも何も分かっていない
のどちらかだと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/30 15:38

    回答いただき、ありがとうございます。

    ・クラスの設計が間違っていた
    ・そもそも何も分かっていない
    →確かにオーバーライドの際、わざわざstaticなメソッドにする動機はないと思います・・・。
    ただ、機能的にできてもいいんじゃないかなー?と思い質問をしました。
    確かにクラスとインスタンス自体別物、という認識はあるのですが、コンピュータ目線で見ると、メソッドをstaticなメソッドとして再定義しても、メソッドを特定できそうな気がしたので疑問に思っていました。

    キャンセル

+2

なぜ、staticなメソッドに変更してはいけないのでしょうか?

以下のようなコードを考えてみましょう。

SuperClass sup = new SubClass();
sup.method();

SuperClassではmethodがインスタンスメソッドなのでコンパイルを通りますが、仮にSubClassstaticなメソッドになっていたとしたら、sup.method()のようにインスタンスから呼ぶことができなくなってしまいます。

これでは「スーパークラスの型でサブクラスを扱う」ことができなくなってしまうので、メソッドの呼び方が変わるような、staticと非staticを切り替える形のオーバーライドはできません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/31 20:53

    余談ですが、RubyやES6以降のJavaScriptなど、「クラスメソッド=クラスオブジェクトに追加するメソッド」という言語の場合、同じ名前のクラスメソッドとインスタンスメソッドが共存できます(継承関係で追加しても単なる追加になるだけで、オーバーライドになりません)。

    キャンセル

  • 2020/09/07 07:05

    ご回答ありがとうございます。

    つまり、インスタンスメソッドからstaticなメソッドは使えない、逆もまた然り。なので、ポリモフィズムを実現するために存在するオーバライドにおいて、static修飾子を変更することは許されていない、ということなんですね!
    ありがとうございます!

    キャンセル

checkベストアンサー

+1

staticメソッドは、どのメソッドが呼ばれるかがコンパイル時に静的に決まります。
そういう性質を与えられているから、staticと呼ばれているのです。

https://eow.alc.co.jp/search?q=static&ref=sa

質問のコードのsup.method()の部分は、どのメソッドが呼ばれるかが実行時に動的に決まる部分です。(SuperClass#methodが動的なメソッドですから)
その部分のコードが実行される時にstatic修飾が付いたメソッドが呼ばれる可能性があるなら、それはstaticという名前に反するわけです。

実行時に動的に呼び出し先が決まるのではなく、コンパイル時に静的に呼び出し先が決まるメソッドだからstaticという名前が付いているのです。
「インスタンスフィールドにアクセスしないからstaticメソッドでも機能面に異常はない」という考えは順序が逆なのです。
「コンパイル時に静的に呼び出し先が決まるという機能を持ったメソッドが欲しいから、そのためにはインスタンスフィールドにアクセスしてはならない」という順序なのです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/07 07:32

    ご回答いただきありがとうございます。
    static修飾子について考えた時、確かにルールに反すると思いました。
    その観点から行くと、static修飾子の変更はコンパイルエラーになりますね。
    腑に落ちました!
    ありがとうございますした!

    キャンセル

+1

see: https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8

8.4.8.1. Overriding (by Instance Methods)
...
It is a compile-time error if an instance method overrides a static method.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/30 15:35 編集

    ご回答ありがとうございます。
    該当箇所を読み込み、javaのリファレンスでも非staticなメソッドをstaticなメソッドに変更してオーバーライドすることは禁止とされているということは理解しました。
    ただ、理由に関していまいちピンとくる箇所がなく、教えていただいたサイトを読み解いていこうと思います。
    ありがとうございます。

    キャンセル

+1

オーバーライドができない理屈については他の回答や教科書を参照してもらいたいですが、「ダメな理由探し」をしたいのであれば

また、今回のメソッドに関しては非staticメンバ変数を参照していないので、インスタンスメソッドでも非staticメソッドでも、機能面に異常はないように思えます。

「今回」と言っているので自覚があると思いますが、非staticなメンバ(インスタンスメンバ)を参照していたらオーバーライドが不可能だという事はわかっているんですよね?

であれば、そもそも派生元のメソッドの中身次第でオーバーライドの可否が決まるのが、正しい言語設計だと思いますか?

また、当初は非staticメンバへの参照がなかったとして、派生元が修正されて非staticなメンバを参照することになった場合にどうなると思いますか?

「非staticメンバ変数を参照していない」というのはあくまで「現時点では」という話ですよね。将来的にもそれが保証されることがありえますか?目の前のコードだけでなく、世界中のすべてのコードで。無理ですよね。

「非staticメンバ変数を参照していない」→「非staticメンバ変数を参照している」への変更があった場合、この継承関係は破壊されます。

派生先の外部設計が変更されたのならまだしも、内部のメソッドの記述が変わるだけで継承関係を壊すような事になるのはおかしいと思いませんか?

オブジェクト指向は属性(プロパティ)と振る舞い(メソッド)に基づいているわけで、実際のメソッド内の処理がどのようなものであれ、外側から見たメソッドには何の変更もないのに、継承関係が壊れるというのはそもそもあってはなりません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/07 07:33

    確かに今回はたまたま、非staticなメンバを参照していないだけで、今後どうなるかは分からないですね。
    その観点からいくと、確かにstatic修飾子の変更はダメなことだと思いました。
    ありがとうございます。

    キャンセル

0

そもそもstaticはインスタンスにひもづかないので、オーバーライドする/されるようなものじゃないです。

以下は、コンパイルエラーが出なくなりますが、想定と異なる動きじゃないでしょうか?
※想定と異なる=オーバーライドされない

public class Sample {
    public static void main(String[] args) {
        SuperClass sup = new SubClass();
        sup.method(); // 警告 static メソッドのアクセス方法
    }
}
class SuperClass {
    public static void method() {        // static を追加
        System.out.println("親クラス");
    }
}
class SubClass extends SuperClass {
    public static void method() {
        System.out.println("子クラス");
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/30 15:40

    ご回答いただきありがとうございます。

    そもそもオーバーライドのルール的にインスタンスの持ち物である非staticなメソッドしか対処としていない。だから、クラスの持ち物であるstaticなメソッドが出てきたらコンパイルエラーとなる、といった認識でしょうか?

    キャンセル

  • 2020/08/31 16:51

    オーバーライドされないのならポリモーフィズムが働かない。static メソッドは定義したクラスの型でしか実行できません。一方、ポリモーフィズムはスーパークラスの型で機能します。この点を深く考えてみませう。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る