非静的内部クラスに静的メソッドを含めることができないのはなぜですか?
内部クラスを静的にすると、機能します。どうして?
static
」と記載されています。)
非静的内部クラスに静的メソッドを含めることができないのはなぜですか?
内部クラスを静的にすると、機能します。どうして?
static
」と記載されています。)
回答:
内部クラスのインスタンスは外部クラスのインスタンスに暗黙的に関連付けられているため、静的メソッド自体を定義することはできません。静的なネストされたクラスは、それを囲むクラスで定義されたインスタンス変数またはメソッドを直接参照できないため、オブジェクト参照を通じてのみそれらを使用できるため、静的なネストされたクラスで静的メソッドを宣言しても安全です。
A.B.sync(X)
または(A内から)を呼び出すことができないのB.sync(x)
ですか?
非静的内部クラスで静的メソッドを許可する意味はあまりありません。どのようにアクセスしますか?(少なくとも最初は)非静的内部クラスインスタンスにアクセスするには、外部クラスインスタンスを経由する必要があります。非静的な内部クラスを作成するための純粋に静的な方法はありません。
外部クラスのOuter
場合、次のtest()
ような静的メソッドにアクセスできます。
Outer.test();
静的内部クラスのInner
場合、次のinnerTest()
ように静的メソッドにアクセスできます。
Outer.Inner.innerTest();
ただし、Inner
が静的でない場合、メソッドを参照するための純粋に静的な方法はありませんinnertest
。非静的内部クラスは、外部クラスの特定のインスタンスに関連付けられています。関数は定数とは異なり、への参照Outer.Inner.CONSTANT
は、関数呼び出しOuter.Inner.staticFunction();
がそうではないように明確であることが保証されています。に定義されてInner.staticFunction()
いる呼び出しgetState()
があるとしOuter
ます。その静的関数を呼び出そうとすると、Innerクラスへのあいまいな参照ができます。つまり、内部クラスのどのインスタンスで静的関数を呼び出しますか?それは重要です。外部オブジェクトへの暗黙的な参照のため、静的メソッドを参照する本当に静的な方法はありません。
Paul Belloraは、言語設計者がこれを許可した可能性があると考えています。次に、非静的内部クラスの静的メソッドで外部クラスへの暗黙的な参照へのアクセスを慎重に禁止する必要があります。この時点で、静的な場合を除いて外部クラスを参照できない場合、これが内部クラスであることの価値は何ですか?静的アクセスが問題ない場合は、内部クラス全体を静的に宣言してみませんか?単に内部クラス自体を静的にすると、外部クラスへの暗黙的な参照がなくなり、このあいまいさがなくなります。
非静的内部クラスで静的メソッドが実際に必要な場合は、おそらく設計を再考する必要があります。
Outer.Inner i = new Outer().new Inner();
、内部クラスは、JLS§15.28に従って静的定数を宣言できます。
Outer.Inner.staticMethod()
アクセスできるように電話をかけますOuter.Inner.CONSTANT
。「外部クラスインスタンスを経由しないと、静的でない内部クラスインスタンスにアクセスできません。」なぜインスタンスが必要なのですか?Outer
を呼び出すためにのインスタンスは必要ありませんOuter.staticMethod()
。私はこれがごちゃごちゃしていることを知っていますが、私の主張は、このようにあなたの答えを組み立てるのは意味がないということです。言語デザイナーが望むなら、私はそれを許可することができました。
Outer.Inner.CONSTANT
とは、Outer.Inner.staticMethod()
定数への参照が暗黙のインスタンス参照のチャンスがないことであるOuter
でInner
インスタンス化されたが。すべての参照Outer.staticMethod()
は、まったく同じ状態を共有します。すべての参照Outer.Inner.CONSTANT
は、まったく同じ状態を共有します。ただし、への参照Outer.Inner.staticMethod()
があいまいですInner
。の各インスタンスの外部クラスへの暗黙的な参照のため、「静的」状態は真に静的ではありません。アクセスするための、明確で静的な方法はありません。
Outer.this
。内部クラスのすべてが外側のクラスのコンテキスト内にある必要があるため、内部クラスで静的メソッドまたは非final静的フィールドを許可する理由はないというJava言語の設計者に同意します。
私には理論があり、それは正しいかもしれないし、正しくないかもしれません。
まず、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
非静的なので、明らかに機能しません。この種の理由は、静的メソッドが許可されない理由(囲んでいるオブジェクトを参照しない限り、静的メソッドを許可するという引数を作成することはできます)と、最終でない静的フィールドを使用できない理由(異なるオブジェクトの非静的内部クラスのインスタンスが「静的状態」を共有する場合、直感に反することになります。また、最終フィールドが許可される理由についても説明します(囲んでいるオブジェクトを参照しない限り)。
public static double sinDeg(double theta) { ... }
、内部の数学ユーティリティクラスを記述したい場合はどうなりますか?
唯一の理由は「必須ではない」ので、なぜそれをサポートする必要があるのでしょうか。
構文的には、内部クラスが静的メンバーを持つことを禁止する理由はありません。のインスタンスはのインスタンスにInner
関連付けられていますがOuter
、javaがそうすることを決定した場合でもOuter.Inner.myStatic
、静的メンバーを参照するために使用できInner
ます。
のすべてのインスタンス間で何かを共有する必要がある場合はInner
、それらをOuter
静的メンバーとして配置できます。これは、中にあなたが静的メンバを使用するよりも悪化していないInner
場合は、Outer
まだのいずれかのプライベートメンバにアクセスすることができますInner
(カプセル化を改善しません)とにかく。
Inner
1つのouter
オブジェクトによって作成されたすべてのインスタンス間で何かを共有する必要がある場合は、それらをOuter
通常のメンバーとしてクラスに入れる方が理にかなっています。
「静的なネストされたクラスはほとんどトップレベルのクラスにすぎない」という意見には同意しません。外部クラスのプライベートメンバーにアクセスできるため、静的にネストされたクラス/内部クラスを外部クラスの一部と見なす方がよいと思います。また、外部クラスのメンバーも「内部クラスのメンバー」です。したがって、内部クラスで静的メンバーをサポートする必要はありません。外部クラスの通常の/静的メンバーで十分です。
送信元:https : //docs.oracle.com/javase/tutorial/java/javaOO/nested.html
インスタンスメソッドと変数の場合と同様に、内部クラスは、その包含クラスのインスタンスに関連付けられ、そのオブジェクトのメソッドとフィールドに直接アクセスできます。また、内部クラスはインスタンスに関連付けられているため、静的メンバー自体を定義することはできません。
オラクルの説明は表面的なものであり、波打つものです。内部クラス内の静的メンバーをプリエンプトする技術的または構文上の理由がないため(C#などの他の言語では許可されています)、Javaデザイナーの動機は、概念的な好みや技術的な利便性の問題であると考えられました。
これが私の推測です:
トップレベルのクラスとは異なり、内部クラスはインスタンスに依存します。内部クラスインスタンスは、その外部クラスのすべてのインスタンスに関連付けられ、そのメンバーに直接アクセスできます。これがJavaで使用する主な動機です。別の表現:内部クラスは、外部クラスインスタンスのコンテキストでのインスタンス化を目的としています。外部クラスインスタンスがない場合、内部クラスは外部クラスの他のインスタンスメンバーよりも使用可能であってはなりません。これを内部クラスのインスタンス依存の精神と呼びましょう。
(オブジェクト指向ではない)静的メンバーの性質は、(オブジェクト指向である)内部クラスのインスタンス依存の精神と衝突します。これは、外部クラスインスタンスなしで内部クラスの静的メンバーを参照/呼び出すことができるためです。修飾された内部クラス名を使用します。
特に静的変数は、別の方法で問題を起こす可能性があります。外部クラスの異なるインスタンスに関連付けられている内部クラスの2つのインスタンスは、静的変数を共有します。変数は状態のコンポーネントであるため、2つの内部クラスインスタンスは、実際には、関連付けられている外部クラスインスタンスとは無関係に状態を共有します。静的変数がこのように機能することは容認できないことではありません(JavaではOOPの純度への妥協としてJavaでそれらを受け入れます)が、インスタンスがすでに外部クラスインスタンスと結合されている内部クラスでそれらを許可することにより、より深い違反が発生することは間違いありません。意図的に。インスタンス依存の精神を支持して内部クラス内の静的メンバーを禁止すると、このより深いOOPオフェンスを先取りするという追加のボーナスが得られます。
一方、そのような違反は静的定数によって引き起こされることはありません。静的定数は意味のある状態を構成しないため、これらは許容されます。インスタンス依存の精神との最大の一貫性のために静的定数を禁止しないのはなぜですか おそらく、定数は必要以上に多くのメモリを使用する必要がないためです(それらが非静的であることが強制される場合、それらは潜在的に無駄なすべての内部クラスインスタンスにコピーされます)。そうでなければ、例外の理由を想像することはできません。
堅実な推論ではないかもしれませんが、IMOは、この問題に関するOracleの大まかな発言を最も理解します。
短い答え:ほとんどのプログラマーがスコープのしくみについて持っているメンタルモデルは、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は静的スコープとインスタンススコープを分割し、ブランチ階層と再結合スコープの階層をサポートするか、単純なブール型の「静的コンテキスト」の考えを拡張して、すべてのレベルでコンテキストのタイプを追跡するように変更する必要があります。現在のスコープ内のネストされたクラスの。
非静的内部クラスに静的メソッドを含めることができないのはなぜですか?
注:非静的なネストされたクラスは内部クラスと呼ばれるため、そうではありませんnon-static inner class
。
内部クラスのインスタンスは、外部クラスの対応するインスタンスがなければ存在しません。内部クラスは、コンパイル時定数以外の静的メンバーを宣言できません。もしそれが許されれば、の意味について曖昧さがあったでしょうstatic
。その場合、特定の混乱があったでしょう。
そのため、設計者はおそらくこの問題をまったく処理しないと決定しました。
内部クラスを静的にすると、機能します。どうして ?
この場合も、内部クラスを静的にすることはできません。静的クラスをネストとして宣言できます。その場合、このネストされたクラスは実際には外部クラスの一部であり、問題なく静的メンバーを持つことができます。
このトピックは多くの人から注目を集めていますが、最も簡単な用語で説明しようと思います。
まず、http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1を参照すると、最初の発生/呼び出しの直前にクラスまたはインターフェースが初期化されますstaticキーワードが前にあるメンバーの。
したがって、内部クラス内の静的メンバーに我慢すると、内部クラスの初期化が行われますが、必ずしも外部/外側のクラスではありません。したがって、クラスの初期化シーケンスを妨害します。
また、静的ではない内部クラスが、囲んでいる/外部クラスのインスタンスに関連付けられていることも考慮してください。したがって、インスタンスに関連付けることは、内部クラスが外部クラスインスタンス内に存在し、インスタンス間で異なることを意味します。
ポイントを単純化して、静的メンバーにアクセスするには、外部クラスのインスタンスが必要です。外部クラスのインスタンスから、非静的内部クラスのインスタンスを作成する必要があります。静的メンバーはインスタンスにバインドされていないため、コンパイルエラーが発生します。
外部クラスの2つのインスタンスがあり、それらの両方が内部クラスをインスタンス化したとします。内部クラスに静的メンバーが1つある場合、そのメンバーのコピーはヒープ領域に1つだけ保持されます。この場合、外部クラスの両方のオブジェクトがこれを参照します。シングルコピー&それらは一緒に変更できます。これにより、「ダーティリード」の状況が発生する可能性があるため、このJavaがこの制限を適用しないようにします。この引数をサポートするもう1つの強力な点は、Javaがここで最終的な静的メンバーを許可することです。いずれかの外部クラスオブジェクトから変更されました。私が間違っていたら私にさせてください。
まず第一に、なぜ誰かが非静的内部クラスで静的メンバーを定義したいのですか?答えは、外部クラスメンバーが内部クラス名のみの静的メンバーを使用できるようにするためです。
ただし、この場合、外部クラスでメンバーを直接定義できます。これは、外部クラスインスタンス内で、内部クラスのすべてのオブジェクトに関連付けられます。
以下のコードのように、
public class Outer {
class Inner {
public static void method() {
}
}
}
このように書くことができます
public class Outer {
void method() {
}
class Inner {
}
}
したがって、コードを複雑にしないように私の考えでは、Javaデザイナーはこの機能を許可していません。
クラスを通常のフィールドとして扱うようにしてください、そうすれば理解できるでしょう。
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
そもそも内部クラスメンバーにアクセスできないため、静的として内部クラスメンバーを使用しても意味がありません。
これについて考えてください。静的メンバーにアクセスするには、className.memberNameを使用します。この場合、outerclassName.innerclassName.memberNameのようになります。これで、innerclassが静的でなければならない理由がわかります。