web-dev-qa-db-ja.com

(非静的)内部クラスに静的メソッドを使用できないのはなぜですか?

なぜ非静的内部クラスに静的メソッドがないのですか?

内部クラスを静的にすると機能します。どうして ?

140
Rahul Garg

内部クラスのインスタンスは、外部クラスのインスタンスに暗黙的に関連付けられているため、静的メソッド自体を定義することはできません。静的なネストされたクラスは、それを囲むクラスで定義されたインスタンス変数またはメソッドを直接参照できないため、オブジェクト参照を通じてのみ使用できます。静的なネストされたクラスで静的メソッドを宣言しても安全です。

108
Bill the Lizard

非静的内部クラスで静的メソッドを許可することはあまり意味がありません。どのようにアクセスしますか?外部クラスインスタンスを経由せずに、(少なくとも最初は)非静的内部クラスインスタンスにアクセスすることはできません。非静的内部クラスを作成するための純粋に静的な方法はありません。

外部クラスOuterの場合、次のように静的メソッドtest()にアクセスできます。

_Outer.test();
_

静的内部クラスInnerの場合、静的メソッドinnerTest()に次のようにアクセスできます。

_Outer.Inner.innerTest();
_

ただし、Innerが静的でない場合、メソッドinnertestを参照する純粋に静的な方法はありません。非静的内部クラスは、外部クラスの特定のインスタンスに関連付けられています。関数は、関数呼び出しOuter.Inner.staticFunction();がそうではない方法で_Outer.Inner.CONSTANT_への参照が明確であることを保証されるという点で、定数とは異なります。 Outerで定義されているInner.staticFunction()を呼び出すgetState()があるとします。その静的関数を呼び出そうとすると、Innerクラスへのあいまいな参照ができます。つまり、内部クラスのどのインスタンスで静的関数を呼び出しますか?重要です。外側のオブジェクトへの暗黙的な参照のため、その静的メソッドを参照するための真に静的な方法はありません。

ポール・ベローラは、言語設計者がこれを許可したかもしれないことは正しいです。その後、非静的内部クラスの静的メソッドで外部クラスへの暗黙的な参照へのアクセスを慎重に禁止する必要があります。この時点で、静的を除いて外部クラスを参照できない場合、これが内部クラスであるとどのような値になりますか?静的アクセスが問題ない場合、内部クラス全体を静的に宣言してみませんか?単純に内部クラス自体を静的にすると、外部クラスへの暗黙的な参照がなくなり、このあいまいさがなくなります。

実際にneed非静的内部クラスの静的メソッドを使用する場合、おそらく設計を再考する必要があります。

45
Eddie

私には理論があるが、それは正しいかもしれないし、そうでないかもしれない。

最初に、Javaで内部クラスがどのように実装されるかについていくつか知っておく必要があります。このクラスがあるとします:

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(); }
}

コンパイルすると、生成されたバイトコードは次のように記述したように見えます。

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(this); }
}

さて、非静的内部クラスで静的メソッドを許可した場合、このようなことをしたいかもしれません。

class Outer {
    private int foo = 0;
    class Inner {
        public static void incrFoo(){ foo++; }
    }
}

... InnerクラスにはOuterインスタンスごとに1つのインカネーションがあるように見えるので、かなり合理的に見えます。しかし、上で見たように、非静的な内部クラスは実際には静的な「内部」クラスの単なる構文上のシュガーであるため、最後の例はほぼ次のようになります。

class Outer {
    private int foo = 0;
    static class Inner {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public static void incrFoo(){ this$0.foo++; }
    }
}

... this$0は非静的です。この種の説明は、静的メソッドが許可されない理由(囲んでいるオブジェクトを参照しない限り静的メソッドを許可できるという議論をすることもできます)、および非最終静的フィールドを使用できない理由(異なるオブジェクトの非静的内部クラスのインスタンスが「静的状態」を共有している場合、直感に反します。また、最終フィールドareが許可されている理由も説明します(囲んでいるオブジェクトを参照しない限り)。

20
gustafc

唯一の理由は「必須ではない」ので、なぜそれをサポートするのが面倒なのでしょうか?

構文的には、内部クラスが静的メンバーを持つことを禁止する理由はありません。 InnerのインスタンスはOuterのインスタンスに関連付けられていますが、Outer.Inner.myStaticを使用してInnerの静的メンバーを参照することもできますJavaそうすることを決めた。

Innerのすべてのインスタンス間で何かを共有する必要がある場合は、それらを静的メンバーとしてOuterに入れるだけです。これは、Innerで静的メンバーを使用するよりも悪くありません。Outerは、Innerのプライベートメンバーに引き続きアクセスできます(カプセル化は改善されません)。

1つのInnerオブジェクトによって作成されたouterのすべてのインスタンス間で何かを共有する必要がある場合は、それらを通常のメンバーとしてOuterクラスに入れる方が理にかなっています。

「静的にネストされたクラスはほとんどトップレベルのクラスにすぎない」という意見には同意しません。外部クラスのプライベートメンバーにアクセスできるため、静的なネストされたクラス/内部クラスを外部クラスの一部と実際に見なす方が良いと思います。また、外部クラスのメンバーも「内部クラスのメンバー」です。したがって、内部クラスで静的メンバーをサポートする必要はありません。アウタークラスの通常/静的メンバーで十分です。

6
Don Li

簡単な答え:ほとんどのプログラマーがスコープがどのように機能するかについてのメンタルモデルは、javacが使用するモデルではありません。より直感的なモデルを一致させるには、javacの動作に大きな変更が必要になります。

内部クラスの静的メンバーが望ましい主な理由は、コードのクリーン化のためです。内部クラスのみが使用する静的メンバーは、外部クラスに配置する必要はなく、内部に存在する必要があります。考慮してください:

class Outer {
   int outID;

   class Inner {
      static int nextID;
      int id = nextID++;

      String getID() {
         return outID + ":" + id;
      }
   }
}

修飾されていない識別子「outID」を使用するとき、getID()で何が起こっているかを考えてください。この識別子が表示されるスコープは次のようになります。

Outer -> Inner -> getID()

ここでも、これはjavacの動作方法に過ぎないため、スコープの「外側」レベルには、外側の静的メンバーとインスタンスメンバーの両方が含まれます。通常、クラスの静的な部分をスコープの別のレベルと考えるように言われるため、これは混乱を招きます。

Outer static -> Outer instance -> instanceMethod()
         \----> staticMethod()

この考え方では、もちろんstaticMethod()はOuterの静的メンバーのみを見ることができます。しかし、それがjavacの仕組みである場合、静的メソッドでインスタンス変数を参照すると、「名前を解決できません」というエラーが発生します。実際に発生するのは、名前がスコープ内で検出されることですが、追加レベルのチェックが開始され、名前がインスタンスコンテキストで宣言され、静的コンテキストから参照されていることがわかります。

OK、これはどのように内部クラスに関係していますか?単純に、内部クラスが静的スコープを持つことができない理由はないと思います。スコープを次のように描いているからです。

Outer static -> Outer instance -> Inner instance -> getID()
         \------ Inner static ------^

つまり、内部クラスの静的宣言と外部クラスのインスタンス宣言は両方とも内部クラスのインスタンスコンテキスト内のスコープ内にありますが、これらのどちらも実際には他方にネストされていません。代わりに、両方がOuterの静的スコープにネストされます。

それはjavacの仕組みではありません。静的メンバーとインスタンスメンバーの両方に単一レベルのスコープがあり、スコープは常に厳密にネストされます。継承でさえ、スーパークラスのスコープを分岐して検索するのではなく、サブクラスに宣言をコピーすることで実装されます。

内部クラスの静的メンバーをサポートするには、javacは静的スコープとインスタンススコープを分割する必要がありますおよびスコープ階層の分岐と再結合をサポートするか、拡張する必要があります現在のスコープ内のネストされたクラスのすべてのレベルでコンテキストのタイプを追跡するために変更する単純なブール「静的コンテキスト」のアイデア。

3

なぜ非静的内部クラスに静的メソッドがないのですか?

注:静的ではないネストされたクラスは内部クラスと呼ばれるため、non-static inner class など。

内部クラスインスタンスは、外部クラスの対応するインスタンスなしでは存在しません。内部クラスは、コンパイル時定数以外の静的メンバーを宣言できません。許可されていれば、staticの意味についてあいまいさがあったでしょう。その場合、特定の混乱があったでしょう。

  1. VMにインスタンスが1つしかないということですか?
  2. または、外部オブジェクトごとに1つのインスタンスのみですか?

そのため、設計者はおそらくこの問題をまったく処理しないという決定を下したのでしょう。

内部クラスを静的にすると機能します。どうして ?

繰り返しますが、内部クラスを静的にすることはできず、静的クラスをネストされたものとして宣言できます。その場合、このネストされたクラスは実際には外部クラスの一部であり、問​​題なく静的メンバーを持つことができます。

3
i_am_zero

このトピックは多くの人から注目を集めていますが、それでも最も簡単な用語で説明しようとします。

まず、 http://docs.Oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 を参照すると、クラスまたはインターフェイスが初期化されますstaticキーワードが先行するメンバーの最初の発生/呼び出しの直前。

  1. したがって、内部クラス内に静的メンバーを配置すると、必ずしも外部クラス/囲みクラスではなく、内部クラスの初期化が行われます。そのため、クラスの初期化シーケンスを妨害します。

  2. また、非静的な内部クラスが、囲んでいる/外部クラスのインスタンスに関連付けられているという事実も考慮してください。したがって、インスタンスに関連付けるとは、内部クラスが外部クラスインスタンス内に存在し、インスタンス間で異なることを意味します。

静的メンバーにアクセスするには、Outerクラスのインスタンスが必要です。このクラスから、非静的内部クラスのインスタンスを作成する必要があります。静的メンバーはインスタンスにバインドされることは想定されていないため、コンパイルエラーが表示されます。

3
Doomed93

From: https://docs.Oracle.com/javase/tutorial/Java/javaOO/nested.html

インスタンスのメソッドおよび変数と同様に、内部クラスはその包含クラスのインスタンスに関連付けられ、そのオブジェクトのメソッドおよびフィールドに直接アクセスします。また、内部クラスはインスタンスに関連付けられているため、静的メンバー自体を定義することはできません。

オラクルの説明は表面的なものであり、波打つものです。内部クラス内の静的メンバーをプリエンプトする技術的または構文上の理由はないため(C#などの他の言語で許可されています)Javaデザイナーの動機は概念的な趣味および/または技術的な問題でした便利。

私の推測は次のとおりです。

最上位クラスとは異なり、内部クラスはインスタンス依存です。内部クラスインスタンスは、その外部クラスのすべてのインスタンスに関連付けられ、そのメンバーに直接アクセスします。これがJavaでそれらを使用する主な動機です。別の言い方をすると、内部クラスはmeant for外部クラスインスタンスのコンテキストでインスタンス化されます。外部クラスインスタンスがなければ、内部クラスは、外部クラスの他のインスタンスメンバーよりもsableであってはなりません。これを、内部クラスのインスタンス依存スピリットと呼びましょう。

静的メンバー(オブジェクト指向ではない)の本質は、内部クラスのインスタンス依存精神と衝突します(これはISオブジェクト指向)修飾された内部クラス名を使用して、外部クラスインスタンスなしで内部クラスの静的メンバーを参照/呼び出すことができるため。

特に静的変数は、さらに別の方法で気分を害する可能性があります。外部クラスの異なるインスタンスに関連付けられている内部クラスの2つのインスタンスは、静的変数を共有します。変数は状態のコンポーネントであるため、2つの内部クラスインスタンスは、実際には、関連付けられている外部クラスインスタンスとは無関係に状態を共有します。静的変数がこのように機能することは受け入れられないというわけではありません(Java OOP純度)への賢明な妥協として受け入れますが、おそらくより深い違反があります)インスタンスが既に設計により外部クラスインスタンスと結合されている内部クラスでそれらを許可することにより、インスタンス依存スピリットを優先して内部メンバー内の静的メンバーを禁止するこのより深いOOP offense。

一方、静的定数に伴うそのような攻撃はありません。静的定数は意味のある状態を構成しないため、これらは許容されます。 インスタンス依存スピリットとの一貫性を最大限にするために、静的定数を禁止しないのはなぜですか?おそらく、constantsが必要以上にメモリを消費する必要がないためです(非静的にする必要がある場合、すべての内部クラスインスタンスにコピーされ、無駄になる可能性があります)。そうでなければ、例外の理由を想像することはできません。

それは確固たる推論ではないかもしれませんが、IMOはこの問題に関するオラクルの大まかな発言の最も意味があります。

内部クラスは静的なネストされたクラスとは完全に異なるものですが、どちらも構文が似ています。静的なネストされたクラスはグループ化の手段にすぎませんが、内部クラスは外部クラスと強い関連性を持ち、すべての値にアクセスできます。内部クラスを使用する理由を確認してから、どちらを使用する必要があるかが自然になるはずです。静的メソッドを宣言する必要がある場合は、おそらく静的なネストされたクラスが必要です。

2
B.E.

まず、誰かが静的メンバーを非静的内部クラスで定義したいのはなぜですか?答えは、外側のクラスメンバーが内側のクラス名を持つ静的メンバーのみを使用できるようにするためです。

ただし、この場合、外部クラスのメンバーを直接定義できます。これは、外部クラスインスタンス内の内部クラスのすべてのオブジェクトに関連付けられます。

以下のコードのように、

public class Outer {

  class Inner {

    public static void method() {

    }

  }

}

このように書くことができます

public class Outer {

  void method() {

   }

   class Inner {


  }

}

したがって、コードを複雑にしないために私の意見ではJavaデザイナーはこの機能を許可していないか、将来のリリースでこの機能がさらに機能を追加する可能性があります。

0
Vishnu Saini

クラスを通常のフィールドとして扱うようにすると、理解できます。

//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something 
0
user1888955

外側のクラスのインスタンスが2つあり、両方とも内側のクラスをインスタンス化するとします。内側のクラスに静的メンバーが1つある場合、ヒープの領域にそのメンバーのコピーを1つだけ保持します。この場合、外側のクラスの両方のオブジェクトはこれを参照しますシングルコピーと一緒に変更できます。これにより、「ダーティリード」状況が発生するため、これを防ぐことができますJavaがこの制限を適用しました。この引数をサポートするもう1つの強みは、Javaは、ここで、外部クラスオブジェクトのいずれからも値を変更できない最終的な静的メンバーを許可します。

0
Malay