LinkedList
のJavaコードを見ていましたが、静的なネストされたクラスEntry
を使用していることに気付きました。
public class LinkedList<E> ... {
...
private static class Entry<E> { ... }
}
通常の内部クラスではなく、静的にネストされたクラスを使用する理由は何ですか?
考えられる唯一の理由は、Entryがインスタンス変数にアクセスできないため、OOPの観点から見ると、カプセル化の方が優れているからです。
しかし、他の理由、おそらくパフォーマンスがあるかもしれないと思った。何だろう?
注意。私の用語が正しいことを願っています、私はそれを静的な内部クラスと呼んでいたでしょうが、これは間違っていると思います: http://Java.Sun.com/docs/books/tutorial/Java/javaOO/ nested.html
リンク先のSunページには、2つの重要な違いがあります。
ネストされたクラスは、それを囲むクラスのメンバーです。非静的なネストされたクラス(内部クラス)は、プライベートであると宣言されている場合でも、囲んでいるクラスの他のメンバーにアクセスできます。静的にネストされたクラスは、包含するクラスの他のメンバーにアクセスできません。
...注:入れ子になった静的クラスは、他のトップレベルクラスと同様に、その外部クラス(および他のクラス)のインスタンスメンバーと対話します。 実質的に、静的なネストされたクラスは、パッケージの利便性のために、別のトップレベルのクラスにネストされたトップレベルのクラスです。
LinkedList.Entry
がトップレベルのクラスである必要はありません。onlyはLinkedList
によって使用されます(静的にネストされた他のインターフェイスもあります) Map.Entry
などのEntry
という名前のクラス-同じ概念)。また、LinkedListのメンバーにアクセスする必要がないため、静的であることが理にかなっています。これははるかにクリーンなアプローチです。
Jon Skeetが指摘 のように、ネストされたクラスを使用している場合は、静的な状態で開始し、次に非静的にする必要があるかどうかを決定することをお勧めしますあなたの使用法。
私の考えでは、質問はあなたが内部クラスを見たときは常に逆でなければならない-それは本当に、内部クラスである必要がある余分な複雑さと、包含クラスのインスタンスへの暗黙的(明示的で明確なIMOではなく)参照?
気を付けてください、私はC#ファンとして偏見があります-C#には内部クラスに相当するものはありませんが、ネストされた型はあります。私はまだ内部クラスを見逃したとは言えません:)
ここで考慮すべき非自明なメモリ保持の問題があります。非静的内部クラスは、その「外部」クラスへの暗黙的な参照を保持するため、内部クラスのインスタンスが強く参照される場合、外部インスタンスも強く参照されます。これは、外部クラスがガベージコレクションされていない場合、出現するがそれを参照していない場合でも、頭をひっかくことがあります。
1つには、非静的内部クラスには、外部クラスのインスタンスを指す追加の非表示フィールドがあります。そのため、Entryクラスが静的ではない場合、必要のないアクセスに加えて、3つではなく4つのポインターを持ち歩くことになります。
原則として、基本的にCの「構造体」のようなデータメンバーのコレクションとして機能するクラスを定義する場合は、静的にすることを検討してください。
静的内部クラスは、ビルダーパターンで使用されます。静的内部クラスは、プライベートコンストラクターのみを持つ外部クラスをインスタンス化できます。したがって、静的な内部クラスを使用して、プライベートコンストラクターのみを持つ外部クラスをインスタンス化できます。 内部クラスにアクセスする前に外部クラスのオブジェクトを作成する必要があるため、内部クラスで同じことはできません。
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
静的なネストされたクラスは、外部クラスのメンバーにアクセスできないため、他の外部クラスとまったく同じです。
パッケージ化の便宜上、静的なネストされたクラスを読みやすくするために1つの外部クラスにまとめることができます。これ以外に、静的なネストされたクラスの他のユースケースはありません。
そのような使用法の例は、Android R.Java(リソース)ファイルにあります。 AndroidのResフォルダーには、レイアウト(画面デザインを含む)、描画可能フォルダー(プロジェクトに使用される画像を含む)、valuesフォルダー(文字列定数を含む)などが含まれます。
すべてのフォルダーがResフォルダーの一部であるため、Androidツールは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;
}
}
http://docs.Oracle.com/javase/tutorial/Java/javaOO/whentouse.html から:
囲んでいるインスタンスの非パブリックフィールドおよびメソッドへのアクセスが必要な場合は、非静的なネストされたクラス(または内部クラス)を使用します。このアクセスが必要ない場合は、静的なネストされたクラスを使用します。
簡単な例:
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が静的関数である例ではそうではありません)
静的と通常の理由の1つは、クラスローディングに関係しています。親のコンストラクターで内部クラスをインスタンス化することはできません。
PS:「ネスト」と「インナー」は互換性があるといつも理解していました。用語には微妙なニュアンスがあるかもしれませんが、ほとんどのJava開発者はどちらかを理解するでしょう。
非静的内部クラスはメモリリークを引き起こす可能性がありますが、静的内部クラスはそれらに対して保護します。外部クラスが大量のデータを保持している場合、アプリケーションのパフォーマンスが低下する可能性があります。
非静的クラスではなく、静的にネストされたクラスを使用すると、場合によってはスペースを節約できます。たとえば、クラス内に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クラスに1つのComparatorのみが含まれるようにします。
パフォーマンスの違いについては知りませんが、あなたが言うように、静的なネストされたクラスは、囲んでいるクラスのインスタンスの一部ではありません。静的なネストされたクラスを作成する方が、内部クラスであることが本当に必要でない限り、単純なようです。
Javaで常に変数を最終的にする理由は少し似ています。変数が最終でなければ、何か面白いことが起こっていることがわかります。静的にネストされたクラスの代わりに内部クラスを使用する場合、十分な理由があります。