(非静的)内部クラスに静的メソッドを含めることができないのはなぜですか?


142

非静的内部クラスに静的メソッドを含めることができないのはなぜですか?

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


4
今Javaはその古いCOBOLですので:)
SES

肝心なのは:彼らはまだそれを実装していないからです。
intrepidis 2017

1
「非静的インナー」はトートロジーです。
ローン侯爵

内部クラスを他の人に公開したくなく、静的メソッドが含まれていることを希望する場合は、内部クラスに「プライベート」と「静的」の両方の修飾子を配置できます。
Willie Z

内部クラスは本質的に静的ではありません。静的なネスト/メンバークラスと非静的なネスト/メンバークラスを持つことができます。後者は内部クラスとしても知られています。(参照:JLSのセクション8.1.3には、「内部クラスは明示的または暗黙的に宣言されていない入れ子クラスであるstatic」と記載されています。)
Erwin Bolwidt '27

回答:


111

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


7
私は内部クラスがその外部クラスのインスタンスに関連付けられていることを知っており、内部クラス内で静的メンバーを宣言できるようになるのはちょっと役に立たないことを知っていますが、なぜ内部クラスが静的メンバーを宣言できないのかと私はまだ尋ねていますか?
カリーム

15
C ++では可能ですので、これはJava言語のバグです。
工業用抗うつ薬

39
バグという言葉...私はその言葉があなたがそれが何を意味すると思うかを意味するとは思わない。
セス・ネルソン

24
より適切な語句は、「マザートラッカーとして迷惑」です。Javaがこれを許可しない理由を理解しないでください。時々、私は内部クラスが親クラスのプロパティを使用することを望みますが、より良い名前空間のために静的メソッドを保持します。これには本質的に何か問題がありますか?:(
Angad

6
丁度。ユーティリティ内部クラスを書きたいのですが。一部のメソッドは外部クラスにアクセスすることでメリットを得られるため、静的にすることはできませんが、一部のメソッドは単なるユーティリティ関数です。なぜ、A.B.sync(X)または(A内から)を呼び出すことができないのB.sync(x)ですか?
Edward Falk

44

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

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

Outer.test();

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

Outer.Inner.innerTest();

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

Paul Belloraは、言語設計者がこれを許可した可能性があると考えています。次に、非静的内部クラスの静的メソッドで外部クラスへの暗黙的な参照へのアクセスを慎重に禁止する必要があります。この時点で、静的な場合を除いて外部クラスを参照できない場合、これが内部クラスであることの価値は何ですか?静的アクセスが問題ない場合は、内部クラス全体を静的に宣言してみませんか?単に内部クラス自体を静的にすると、外部クラスへの暗黙的な参照がなくなり、このあいまいさがなくなります。

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


6
-1私はあなたがここで取った角度に同意しなければなりません。確かに、内部クラス型を参照できます。たとえばOuter.Inner i = new Outer().new Inner();、内部クラス、JLS§15.28に従って静的定数を宣言できます。
ポールベローラ2013

2
はい、内部クラスは静的定数を宣言できます。それは静的メソッドとは何の関係もありません!静的メソッドを非静的に参照できます、これはお勧めできません。すべてのコード品質ツールは、そのような参照に対して、そして正当な理由で不満を持っています。そして、あなたは私のポイントを逃しました。静的内部クラスを参照する方法はないとは言ったことがありません。非静的外部クラスの内部クラスの静的メソッドを参照する静的な方法はないと述べました。したがって、それを参照する適切な方法はありません。
エディ

25
「非静的内部クラスで静的メソッドを許可する意味はあまりありません。どのようにアクセスしますか?」Outer.Inner.staticMethod()アクセスできるように電話をかけますOuter.Inner.CONSTANT。「外部クラスインスタンスを経由しないと、静的でない内部クラスインスタンスにアクセスできません。」なぜインスタンスが必要なのですか?Outerを呼び出すためにのインスタンスは必要ありませんOuter.staticMethod()。私はこれがごちゃごちゃしていることを知っていますが、私の主張は、このようにあなたの答えを組み立てるのは意味がないということです。言語デザイナーが望むなら、私はそれを許可することができました。
ポールベローラ2013

1
Outer.Inner.CONSTANTとは、Outer.Inner.staticMethod()定数への参照が暗黙のインスタンス参照のチャンスがないことであるOuterInnerインスタンス化されたが。すべての参照Outer.staticMethod()は、まったく同じ状態を共有します。すべての参照Outer.Inner.CONSTANTは、まったく同じ状態を共有します。ただし、への参照Outer.Inner.staticMethod()があいまいですInner。の各インスタンスの外部クラスへの暗黙的な参照のため、「静的」状態は真に静的ではありません。アクセスするための、明確で静的な方法はありません。
Eddie

3
@Eddie静的メソッドではインスタンスフィールドを参照できないため、暗黙的なインスタンスフィールドを参照できないことに関連する競合はありませんOuter.this。内部クラスのすべてが外側のクラスのコンテキスト内にある必要があるため、内部クラスで静的メソッドまたは非final静的フィールドを許可する理由はないというJava言語の設計者に同意します。
セオドアマードック2014

20

私には理論があり、それは正しいかもしれないし、正しくないかもしれません。

まず、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非静的なので、明らかに機能しません。この種の理由は、静的メソッドが許可されない理由(囲んでいるオブジェクトを参照しない限り、静的メソッドを許可するという引数を作成することはできます)と、最終でない静的フィールドを使用できない理由(異なるオブジェクトの非静的内部クラスのインスタンスが「静的状態」を共有する場合、直感に反することになります。また、最終フィールド許可される理由についても説明します(囲んでいるオブジェクトを参照しない限り)。


7
しかし、それは通常の「静的コンテキストから非静的変数にアクセスしようとする」タイプのエラーにすぎません。トップレベルの静的メソッドが独自のクラスのインスタンス変数にアクセスしようとした場合と違いはありません。
Lawrence Dol、

2
構文的には可能であるように見えても、技術的に不可能である理由を実際に説明しているので、このアンサーが好きです。
LoPoBo 2015

@gustafc、それは素晴らしい説明だったと思います。しかし、Lawrenceが指摘しているように、静的ではないfooへの参照のため、それは単なる失敗です。しかしpublic static double sinDeg(double theta) { ... }、内部の数学ユーティリティクラスを記述したい場合はどうなりますか?
Edward Falk

6

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

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

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

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

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


内部クラスも「必須」ではありません。ただし、この言語内部クラスを提供するため、それらの完全で意味のある実装を提供する必要があります。
intrepidis 2017

4

送信元:https : //docs.oracle.com/javase/tutorial/java/javaOO/nested.html

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

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

これが私の推測です:

トップレベルのクラスとは異なり、内部クラスはインスタンスに依存します。内部クラスインスタンスは、その外部クラスのすべてのインスタンスに関連付けられ、そのメンバーに直接アクセスできます。これがJavaで使用する主な動機です。別の表現:内部クラスは、外部クラスインスタンスのコンテキストでのインスタンス化を目的としています。外部クラスインスタンスがない場合、内部クラスは外部クラスの他のインスタンスメンバーよりも使用可能であってはなりません。これを内部クラスのインスタンス依存の精神と呼びましょう。

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

特に静的変数は、別の方法で問題を起こす可能性があります。外部クラスの異なるインスタンスに関連付けられている内部クラスの2つのインスタンスは、静的変数を共有します。変数は状態のコンポーネントであるため、2つの内部クラスインスタンスは、実際には、関連付けられている外部クラスインスタンスとは無関係に状態を共有します。静的変数がこのように機能することは容認できないことではありません(JavaではOOPの純度への妥協としてJavaでそれらを受け入れます)が、インスタンスがすでに外部クラスインスタンスと結合されている内部クラスでそれらを許可することにより、より深い違反が発生することは間違いありません。意図的に。インスタンス依存の精神を支持して内部クラス内の静的メンバーを禁止すると、このより深いOOPオフェンスを先取りするという追加のボーナスが得られます。

一方、そのような違反は静的定数によって引き起こされることはありません。静的定数は意味のある状態を構成しないため、これらは許容されます。インスタンス依存の精神との最大の一貫性のために静的定数を禁止しないのはなぜですか おそらく、定数は必要以上に多くのメモリを使用する必要がないためです(それらが非静的であることが強制される場合、それらは潜在的に無駄なすべての内部クラスインスタンスにコピーされます)。そうでなければ、例外の理由を想像することはできません。

堅実な推論ではないかもしれませんが、IMOは、この問題に関するOracleの大まかな発言を最も理解します。


3

短い答え:ほとんどのプログラマーがスコープのしくみについて持っているメンタルモデルは、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は静的スコープとインスタンススコープ分割し、ブランチ階層と再結合スコープの階層サポートするか、単純なブール型の「静的コンテキスト」の考えを拡張して、すべてのレベルでコンテキストのタイプを追跡するように変更する必要があります。現在のスコープ内のネストされたクラスの。


非静的な内部クラスに非定数の静的メンバーを許可することによるより根本的な問題は、そのようなメンバーを宣言するプログラマーがそれらを外部クラスのインスタンスにバインドさせるか、またはそれらを真に静的にすることである可能性があることです。コンストラクト(合法の場合)が2つの異なるもののいずれかを意味するように明確に指定でき、それらの両方が他の明確な方法で表現できる場合、そのコンストラクトを違法であると指定する方が、どちらかの意味を持つ。
スーパーキャット2014年

3

非静的内部クラスに静的メソッドを含めることができないのはなぜですか?

注:非静的なネストされたクラスは内部クラスと呼ばれるため、そうではありませんnon-static inner class

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

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

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

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

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


3

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

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

  1. したがって、内部クラス内の静的メンバーに我慢すると、内部クラスの初期化が行われますが、必ずしも外部/外側のクラスではありません。したがって、クラスの初期化シーケンスを妨害します。

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

ポイントを単純化して、静的メンバーにアクセスするには、外部クラスのインスタンスが必要です。外部クラスのインスタンスから、非静的内部クラスのインスタンスを作成する必要があります。静的メンバーはインスタンスにバインドされていないため、コンパイルエラーが発生します。


2

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


ベネディクト、「静的なネストされたクラスはグループ化の手段にすぎない」とはどういう意味ですか?
アンクル

0

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


0

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

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

以下のコードのように、

public class Outer {

  class Inner {

    public static void method() {

    }

  }

}

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

public class Outer {

  void method() {

   }

   class Inner {


  }

}

したがって、コードを複雑にしないように私の考えでは、Javaデザイナーはこの機能を許可していません。


0

クラスを通常のフィールドとして扱うようにしてください、そうすれば理解できるでしょう。

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

-1

そもそも内部クラスメンバーにアクセスできないため、静的として内部クラスメンバーを使用しても意味がありません。

これについて考えてください。静的メンバーにアクセスするには、className.memberNameを使用します。この場合、outerclassName.innerclassName.memberNameのようになります。これで、innerclassが静的でなければならない理由がわかります。


-2

静的ネストされたクラスで静的メソッドが許可されています。例えば

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.