Javaの静的ネストクラス、なぜですか?


217

私はJavaコードを見ていて、LinkedListそれが静的なネストされたクラスを利用していることに気づきましたEntry

public class LinkedList<E> ... {
...

 private static class Entry<E> { ... }

}

通常の内部クラスではなく、静的なネストされたクラスを使用する理由は何ですか?

私が考えることができる唯一の理由は、Entryがインスタンス変数にアクセスできないため、OOPの観点から見ると、より良いカプセル化を持っているということです。

しかし、パフォーマンスには他の理由があるのではないかと思いました。それは何でしょうか?

注意。私は私の用語が正しいことを願っています、それを静的内部クラスと呼んだでしょうが、これは間違っていると思います:http : //java.sun.com/docs/books/tutorial/java/javaOO/nested.html


回答:


271

リンク先のSunページには、2つの主な違いがいくつかあります。

ネストされたクラスは、それを囲むクラスのメンバーです。非静的なネストされたクラス(内部クラス)は、それらがプライベートとして宣言されている場合でも、外側のクラスの他のメンバーにアクセスできます。静的ネストクラスは、それを含むクラスの他のメンバーにアクセスできません。
...

注:静的なネストされたクラスは、他のトップレベルクラスと同様に、その外部クラス(および他のクラス)のインスタンスメンバーと対話します。実際、静的なネストされたクラスは、パッケージングの便宜上、別のトップレベルのクラスにネストされた動作としてトップレベルのクラスです。

によってのみ使用されるためLinkedList.Entry、トップレベルのクラスである必要はありません(同じ概念など、静的な入れ子になったクラスもある他のインターフェイスもあります)。また、LinkedListのメンバーにアクセスする必要がないため、静的であることは理にかなっています。これは、よりクリーンなアプローチです。LinkedListEntryMap.Entry

ジョンスキートは指摘し、私はあなたが入れ子になったクラスを使用していること、それは静的なものから始め、その後、それは本当にあなたの使用状況に基づいて非staticにする必要があるかどうかを判断することであるならば、それは良いアイデアだと思います。


フン、私が取得するように見えることができないコメントへのアンカーリンク:仕事にするが、そのこのコメント#comment113712_253507
ザックLysobey

1
@matt b静的なネストされたクラスがOuterクラスのインスタンスメンバーにアクセスできない場合、それはどのようにOuterクラスのインスタンスメンバーと相互作用しますか?
オタク

1
@mattbしかし、どのよう@Geekが気づいた、Sunのページは矛盾している: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class :ドキュメントはと言う前に、どのようにそれはちょうど段落場合に可能である Static nested classes do not have access to other members of the enclosing class かもしれない、彼らが言いたい: A nested (non-static) class interacts with the instance members of its outer class (and other classes) just like any other top-level class
tonix

1
@DavidSリンクをありがとう!ええ、私は間違っていました。私のコメントを読んだところ、私の言い直しが間違っていることがわかりました。あなたが言ったように:An inner class interacts with the instance members through an implicit reference to its enclosing class、およびこの他の興味深い特性ポイントnon-static inner classesだけでなく、anonymous inner classeslocal classes defined inside a block:彼らはすべてのことはできませんno-arg原因コンストラクタをコンパイラが暗黙的に囲んでのインスタンスの参照を渡すために、すべてのコンストラクタの引数列を付加しますクラス。ものすごく単純。
tonix 2015年

1
静的内部クラスを使用して、プライベートコンストラクタのみを持つ外部クラスをインスタンス化できます。これはビルダーパターンで使用されます。同じことを内部クラスで行うことはできません。
seenimurugan 2016年

47

私の考えでは、内部クラスを見るときはいつでも逆の質問になるはずです- 本当に複雑で、インスタンスへの(明示的で明確なIMOではなく)暗黙的な参照を伴う内部クラスである必要がありますか?包含クラスの?

ちなみに、私はC#のファンとして偏っています。C#には、入れ子になった型はありますが、内部クラスに相当するものはありません。私はまだ内部クラスを見逃したとは言えません:)


4
私は間違っている可能性がありますが、内部クラスではなく、静的なネストされたクラスの例のように見えます。例では、ネストされたクラスの周囲のクラスのインスタンス変数にアクセスできないことも指定しています。
ColinD 2008年

うん、コリンの権利-C#には内部クラスがなく、ネストされたクラスがあります。ちなみに、C#の静的な入れ子クラスは、Javaの静的な入れ子クラスとは
異なり

2
ネストされた型は、C#がJavaと比較して非常に正確な領域の1つです。私はいつもそのセマンティック/論理的な正しさに驚嘆...
nawfal

3
@nawfal:はい、C#言語がいかにうまく設計(および指定)されているかに畏敬の念を抱くいくつかの微妙な点を除けば。
Jon Skeet

1
@JonSkeetあなたはそれらの微笑が何であるかについての記事やブログを持っていますか?私はあなたが "niggles"として見つけたものを通って行きたいです:)
nawfal

27

ここで考慮すべき明らかでないメモリ保持の問題があります。非静的内部クラスはその「外部」クラスへの暗黙的な参照を維持するため、内部クラスのインスタンスが強く参照されている場合、外部インスタンスも強く参照されます。外部クラスがガベージコレクションされていない場合、これを参照するものがないように見えても、これにより頭を悩ませる可能性があります


「外部」クラスが最終であり、したがってインスタンス化できない場合、その場合、この引数は意味がありますか?後者が最終的なものである場合、外部クラスへの参照を保持/維持することは役に立たないためです。
getsadzeg

10

1つには、非静的内部クラスには、外部クラスのインスタンスを指す追加の非表示フィールドがあります。したがって、Entryクラスが静的でない場合は、必要のないアクセス権を持っているだけでなく、3つではなく4つのポインタを持ちます。

原則として、Cの「構造体」のように、基本的にそこにデータメンバーのコレクションとして機能するクラスを定義する場合は、静的にすることを検討します。


8

静的内部クラスはビルダーパターンで使用されます。静的内部クラスは、プライベートコンストラクタのみを持つ外部クラスをインスタンス化できます。したがって、静的な内部クラスを使用して、プライベートコンストラクターのみを持つ外部クラスをインスタンス化できます。内部クラスにアクセスする前に外部クラスのオブジェクトを作成する必要があるため、内部クラスで同じことを行うことはできません。

class OuterClass {
    private OuterClass(int x) {
        System.out.println("x: " + x);
    }

    static class InnerClass {
        public static void test() {
            OuterClass outer = new OuterClass(1);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        OuterClass.InnerClass.test();
        // OuterClass outer = new OuterClass(1); // It is not possible to create outer instance from outside.
    }
}

これはxを出力します:1


7

静的ネストクラスは、外部クラスメンバーにアクセスできないため、他の外部クラスと同じです。

パッケージ化の便宜上、静的なネストされたクラスを読みやすくするために1つの外部クラスにまとめることができます。これ以外には、静的なネストされたクラスの他の使用例はありません。

このような使用例は、Android R.java(リソース)ファイルにあります。androidのResフォルダーには、レイアウト(画面デザインを含む)、ドローアブルフォルダー(プロジェクトで使用される画像を含む)、値フォルダー(文字列定数を含む)などが含まれています。

すべてのフォルダーはResフォルダーの一部であるため、androidツールはR.java(リソース)ファイルを生成します。R.java(リソース)ファイルには、内部フォルダーごとに静的なネストされたクラスが多数含まれています。

Androidで生成されたR.javaファイルのルックアンドフィールは次のとおりです。 ここでは、パッケージの便宜のためにのみ使用しています。

/* AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found.  It
 * should not be modified by hand.
 */

package com.techpalle.b17_testthird;

public final class R {
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class layout {
        public static final int activity_main=0x7f030000;
    }
    public static final class menu {
        public static final int main=0x7f070000;
    }
    public static final class string {
        public static final int action_settings=0x7f050001;
        public static final int app_name=0x7f050000;
        public static final int hello_world=0x7f050002;
    }
}


4

簡単な例:

package test;

public class UpperClass {
public static class StaticInnerClass {}

public class InnerClass {}

public static void main(String[] args) {
    // works
    StaticInnerClass stat = new StaticInnerClass();
    // doesn't compile
    InnerClass inner = new InnerClass();
}
}

非静的の場合、クラスは上位クラスのインスタンスで例外としてインスタンス化できません(mainが静的関数である例ではそうではありません)。


StaticInnerClassは、実際には静的なネストされた/内部クラスではありません。これはトップレベルの静的クラスです。
theRiley

2

静的と通常の理由の1つは、クラスローディングに関係しています。親のコンストラクターで内部クラスをインスタンス化することはできません。

PS:私は常に「入れ子」と「内部」が交換可能であることを理解してきました。用語には微妙なニュアンスがあるかもしれませんが、ほとんどのJava開発者はどちらかを理解するでしょう。


1

非静的内部クラスはメモリリークを引き起こす可能性がありますが、静的内部クラスはそれらを保護します。外部クラスが大量のデータを保持している場合、アプリケーションのパフォーマンスが低下する可能性があります。


「静的内部」は、用語で矛盾しています。
ローンの侯爵

1
@ EJP、sheesh ...誰かが「静的内部クラス」について言及するときはいつでも、これを指摘することで人々は本当に降りる...
Sakiboy

0

パフォーマンスの違いについてはわかりませんが、静的なネストクラスは、囲んでいるクラスのインスタンスの一部ではありません。それが本当に内部クラスである必要がない限り、静的なネストされたクラスを作成する方が簡単に思えます。

それは、なぜ私が常にJavaで変数をfinalにするのかと少し似ています-それらがfinalでない場合、何か面白いことが起こっていることを知っています。静的なネストされたクラスの代わりに内部クラスを使用する場合は、十分な理由があります。


内部クラスも「囲んでいるクラスのインスタンスの一部」ではありません。
ローンの侯爵

内部クラスは、存在するクラスに依存しており、存在するクラスのメンバーに密接にアクセスできるため、実際にはクラスの一部です。実際、メンバーです。
theRiley

0

非静的クラスではなく静的ネストクラスを使用すると、場合によってはスペースを節約できます。たとえばComparator、クラス内に実装すると、Studentと言います。

public class Student {
  public static final Comparator<Student> BY_NAME = new ByName();
  private final String name;
  ...
  private static class ByName implements Comparator<Student> {
    public int compare() {...}
  }
}

次に、staticは、新しいStudentインスタンスが作成されるたびに新しいクラスをインスタンス化するのではなく、Studentクラスに1つのコンパレータのみが含まれるようにします。


-1

内部クラスの利点-

  1. 使い捨て
  2. カプセル化をサポートおよび改善
  3. 読みやすさ
  4. プライベートフィールドアクセス

外部クラスが存在しない場合、内部クラスは存在しません。

class car{
    class wheel{

    }
}

内部クラスには4つのタイプがあります。

  1. 通常の内部クラス
  2. メソッドローカル内部クラス
  3. 匿名の内部クラス
  4. 静的内部クラス

ポイント---

  1. 静的内部クラスから、外部クラスの静的メンバーにのみアクセスできます。
  2. 内部クラス内では、静的メンバーを宣言できません。
  3. 外部クラスの静的領域で通常の内部クラスを呼び出すため。

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  4. 外部クラスのインスタンス領域で通常の内部クラスを呼び出すため。

    Inner i=new Inner();

  5. 通常の内部クラスを外部クラスの外で呼び出すため。

    Outer 0=new Outer(); Outer.Inner i= O.new Inner();

  6. 内部クラスの内部この内部クラスへのポインタ。

    this.member-current inner class outerclassname.this--outer class

  7. 内部クラスの適用可能な修飾子は-パブリック、デフォルト、

    final,abstract,strictfp,+private,protected,static

  8. outer $ innerは内部クラス名の名前です。

  9. インスタンスメソッド内の内部クラスでは、静的クラスと外部クラスのインスタンスフィールドにアクセスできます。

10.静的メソッド内の内部クラスの場合、静的フィールドのみにアクセスできます

外部クラス。

class outer{

    int x=10;
    static int y-20;

    public void m1() {
        int i=30;
        final j=40;

        class inner{

            public void m2() {
                // have accees x,y and j
            }
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.