Javaが内部クラスの静的フィールドを禁止するのはなぜですか?


85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

で静的フィールドにアクセスすることはできませんが、静的でOuterClass.InnerClass.iある必要があるもの、たとえば作成されたInnerClassオブジェクトの数を記録する場合は、そのフィールドを静的にすることが役立ちます。では、なぜJavaは内部クラスの静的フィールド/メソッドを禁止しているのでしょうか。

編集:静的なネストされたクラス(または静的な内部クラス)でコンパイラーを満足させる方法を知っていますが、Javaが言語設計と内部クラス(または通常の内部クラス)内の静的フィールド/メソッドを禁止している理由です誰かがそれについてもっと知っているなら、実装の側面。


3
私のお気に入りの例は、内部クラス専用のロガーを使用することです。他のすべてのロガーのように静的にすることはできません。
Piotr Findeisen 2013年

回答:


32

内部クラスの背後にある考え方は、囲んでいるインスタンスのコンテキストで動作することです。どういうわけか、静的変数とメソッドを許可することは、この動機と矛盾しますか?

8.1.2内部クラスとそれを囲むインスタンス

内部クラスは、明示的または暗黙的に静的として宣言されていないネストされたクラスです。内部クラスは静的初期化子(§8.7)またはメンバーインターフェースを宣言できません。内部クラスは、コンパイル時定数フィールド(§15.28)でない限り、静的メンバーを宣言できません。


18
多分それはちょうどそのように決定されました
グレゴリーパコス2009

3
親参照なしで非静的内部をインスタンス化することはできませんが、それでも初期化することはできます
skaffman 2009

ClassLoaderが「クラスXが初期化されました」というキャッシュを保持している場合、そのロジックを使用して、クラス[オブジェクトを表す] Xの複数のインスタンスを初期化することはできません(これは、クラスオブジェクトを複数の内部クラス内の内部クラスとしてインスタンス化する必要がある場合に必要です。個別のオブジェクト)。
アーウィンスマウト2015

@skaffmanそれでも意味がありません。内部クラスの静的プロパティは一度だけ初期化されるので、何が問題になるでしょうか?現在、静的ハッシュマップがあり、このマップのみを操作するメソッドが約4つあるため、すべてを内部クラスにグループ化するのがより理想的です。ただし、静的ハッシュマップは外部に存在する必要があり、場合によっては他の関連するものが必要になります。これはまったく愚かです。静的プロパティを初期化することの問題は何でしょうか?
mmm 2016

54

私が知りたいのは、Javaが内部クラス内の静的フィールド/メソッドを禁止している理由です

これらの内部クラスは「インスタンス」内部クラスであるためです。つまり、それらは囲んでいるオブジェクトのインスタンス属性のようなものです。

これらは「インスタンス」クラスであるため、static機能を許可することは意味がありませんstatic。そもそもインスタンスなしで機能することを目的としています。

これは、静的/インスタンス属性を同時に作成しようとするようなものです。

次の例を見てください。

class Employee {
    public String name;
}

従業員の2つのインスタンスを作成する場合:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

それぞれがプロパティに対して独自の値を持っている理由は明らかnameですよね?

同じことが内部クラスでも起こります。各内部クラスインスタンスは、他の内部クラスインスタンスから独立しています。

したがって、counterクラス属性を作成しようとすると、2つの異なるインスタンス間でその値を共有する方法はありません。

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

インスタンスを作成するとaし、b上記の例では、何が静的変数に正しい値でしょうかcountInnerDataクラスの存在は、囲んでいる各オブジェクトに完全に依存しているため、それを判別することはできません。

そのため、クラスがとして宣言されstaticている場合、それ自体を生きるために、生きているインスタンスはもう必要ありません。依存関係がなくなったので、静的属性を自由に宣言できます。

これは繰り返しのように聞こえると思いますが、インスタンス属性とクラス属性の違いを考えると、理にかなっています。


4
内部クラスの静的プロパティについての説明を購入しますが、@ skaffmanが私の回答へのコメントで指摘しているように、静的メソッドはどうですか?メソッドをインスタンスから切り離すことを強制せずに、メソッドを許可する必要があるようです。実際、Javaでは、インスタンスで静的メソッドを呼び出すことができます(ただし、これは悪いスタイルと見なされます)。ところで:私は同僚にOPのコードをC#としてコンパイルするように依頼しましたが、コンパイルされます。したがって、C#は明らかにこれを許可しており、OPが実行したいことがいくつかの基本的なOOの原則に違反していないことを示しています。
asaph

2
メソッドとまったく同じように起こります。ここでのポイントは、属性メソッドが統計であるかどうかではなく、内部クラス自体がインスタンス「もの」であるという事実です。つまり、ここでの問題は、外部クラスが作成されるまで、そのような内部クラスのインスタンスが存在しないことです。したがって、何もない場合、どのメソッドがディスパッチされますか。そもそもインスタンスが必要になるという理由だけで、あなたは空気にぶつかるでしょう。
OscarRyz 2009

1
C#について...まあ。C#で許可されているからといって、オブジェクト指向プログラミングは有効ではありません。間違っているわけではありませんが、C#には、一貫性を犠牲にしても開発を容易にするためのいくつかのパラダイムが含まれています(.NETリリースごとに新しいことを学ぶ必要があります)。とりわけこれと他の種類のものを許可します。それは良いことだと思います。コミュニティが追加機能が十分にクールだと感じた場合、C#は将来それを持っているかもしれません。
OscarRyz 2009

2
@OscarRyz静的メソッド/フィールドを使用するために内部クラスのインスタンスが必要なのはなぜですか?そして、内部クラスの[便利な]静的メソッドの例プライベートヘルパーメソッドです。
Leonid Semyonov 2014

1
を使用するとfinal、静的フィールドがJavaの内部クラスで許可されます。このシナリオをどのように説明しますか?
番号945 2018

34

InnerClassstatic(のOuterClass)インスタンスに属しているため、メンバーを持つことはできません。インスタンスからデタッチすることを宣言するInnerClassstatic、コードがコンパイルされます。

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

ところで:あなたはまだのインスタンスを作成することができますInnerClassstaticこのコンテキストでは、を囲むインスタンスなしでそれを行うことができますOuterClass


6
InnerClassに属していませOuterClass、そのインスタンスは属します。2つのクラス自体にはそのような関係はありません。なぜ静的メソッドを使用できないのかという疑問はInnerClassまだ残っています。
skaffman 2009

9

実際、静的フィールドが定数であり、コンパイル時に書き込まれる場合は、静的フィールドを宣言できます。

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}

8
  1. クラス初期化シーケンス は重要な理由です。

内部クラスは囲んでいる/外部クラスのインスタンスに依存しているため、外部クラスは内部クラスの初期化の前に初期化する必要があります。
これは、JLSがクラスの初期化について述べていることです。 必要なのは、クラスTは次の場合に初期化されるということです。

  • Tによって宣言された静的フィールドが使用され、フィールドは定数変数ではありません。

したがって、内部クラスにアクセスする静的フィールドがある場合、内部クラスが初期化されますが、それによって、囲んでいるクラスが初期化されることは保証されません。

  1. それはいくつかの基本的なルールに違反するでしょうnoobのものを避けるために、最後のセクション(to two cases)にスキップできます

一つのことについて、いくつかはある、それはあらゆる方法で普通のクラスのように動作すること、また、外部クラスに関連付けられています。static nested classnested classstatic

しかし、Inner class/の概念は、外部/囲みクラスのに関連付けられます。クラスではなくインスタンスに関連付けられていることに注意してください。インスタンスに関連付けるということは、(インスタンス変数の概念から)インスタンス内に存在し、インスタンス間で異なることを明確に意味します。 non-static nested classinstance

ここで、静的なものを作成すると、クラスがロードされるときに初期化され、すべてのインスタンス間で共有される必要があります。しかし、非静的であるため、内部クラス自体(今のところ内部クラスのインスタンスを間違いなく忘れることができます)でさえ、外部/囲んでいるクラスのすべてのインスタンスと共有されているわけではありません(少なくとも概念的には)、どのようにいくつかの変数を期待できますか内部クラスのは、内部クラスのすべてのインスタンス間で共有されます。

したがって、Javaで静的ネストされたクラスではなく静的変数を使用できる場合。が存在することになる2例

  • 内部クラスのすべてのインスタンスと共有されている場合、context of instance(インスタンス変数)の概念に違反します。それならNOです。
  • すべてのインスタンスと共有されていない場合、静的であるという概念に違反します。再びいいえ。

5

この「制限」に最も適していると思う動機は次のとおりです。内部クラスの静的フィールドの動作を、外部オブジェクトのインスタンスフィールドとして実装できます。 したがって、静的フィールド/メソッドは必要ありません。つまり、あるオブジェクトのすべての内部クラスインスタンスがフィールド(またはメソッド)を共有するという動作です。

したがって、すべての内部クラスインスタンスをカウントしたいとすると、次のようになります。

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}

2
しかし、問題は、静的フィールドが宣言されたときに静的フィールドを許可する理由は何finalですか?
慰め2016年

[ stackoverflow.com/a/1954119/1532220](上記のOscarRyzsの回答)を見ると、彼の動機は、値を変数に関連付けることができないということです。もちろん、変数がfinalの場合、割り当てる値を非常に簡単に知ることができます(知っておく必要があります)。
ianos 2016年

2

簡単に言うと、非静的内部クラスは外部クラスのインスタンス変数であり、外部クラスが作成され、実行時に外部クラスオブジェクトが作成され、静的変数がクラスのロード時に作成される場合にのみ作成されます。したがって、非静的内部クラスは実行時のものであるため、静的は非静的内部クラスの一部ではありません。

注:内部クラスは常に外部クラスの変数のように扱い、他の変数と同様に静的または非静的の場合があります。


ただし、内部クラスはstatic final定数を持つことができます。
雨が降る


1

それは「静的」の意味に曖昧さを引き起こすからです。

内部クラスは、コンパイル時定数以外の静的メンバーを宣言できません。「静的」の意味についてはあいまいさがあります。仮想マシンにインスタンスが1つしかないということですか?または、外部オブジェクトごとに1つのインスタンスのみですか?言語設計者は、この問題に取り組まないことに決めました。

Cay S.Horstmannによる「焦りのためのコアJavaSE9」からの抜粋。90ページ2.6.3章


-1

一貫性のためだと思います。技術的な制限はないようですがOuterClass.InnerClass.i、中間のステップが静的ではないため、外部から内部クラスの静的メンバーにアクセスすることはできません。


ただし、内部クラスはstatic final定数を持つことができます。
雨が降る
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.