Javaの内部クラスと静的ネストクラスの主な違いは何ですか?設計/実装は、これらのいずれかを選択する上で役割を果たしますか?
28 に答える
Javaチュートリアルから:
ネストされたクラスは、静的と非静的の2つのカテゴリに分類されます。静的と宣言されたネストされたクラスは、単に静的なネストされたクラスと呼ばれます。非静的なネストされたクラスは、内部クラスと呼ばれます。
静的にネストされたクラスには、囲んでいるクラス名を使用してアクセスします。
OuterClass.StaticNestedClass
たとえば、静的にネストされたクラスのオブジェクトを作成するには、次の構文を使用します。
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
内部クラスのインスタンスであるオブジェクトは、外部クラスのインスタンス内に存在します。次のクラスを検討してください。
class OuterClass {
...
class InnerClass {
...
}
}
InnerClassのインスタンスは、OuterClassのインスタンス内にのみ存在でき、それを囲むインスタンスのメソッドとフィールドに直接アクセスできます。
内部クラスをインスタンス化するには、最初に外部クラスをインスタンス化する必要があります。次に、次の構文を使用して、外部オブジェクト内に内部オブジェクトを作成します。
OuterClass outerObject = new OuterClass()
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
完全を期すために、インスタンスを囲まない内部クラスなどもあることに注意してください。
class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}
ここで、new A() { ... }
は静的コンテキストで定義された内部クラスであり、それを囲むインスタンスはありません。
Javaチュートリアルには次のように書かれています:
用語: ネストされたクラスは、静的と非静的の 2 つのカテゴリに分けられます。静的と宣言された入れ子になったクラスは、単に静的な入れ子になったクラスと呼ばれます。ネストされた非静的クラスは内部クラスと呼ばれます。
一般的に、「ネストされた」と「内部」という用語は、ほとんどのプログラマーによって同じ意味で使用されますが、ここでは内部と静的の両方をカバーする「ネストされたクラス」という正しい用語を使用します。
クラスは無限に入れ子にすることができます。たとえば、クラス A には、クラス D を含むクラス C を含むクラス B を含めることができます。ただし、クラスの入れ子レベルが 1 つ以上になることはまれです。これは一般的に悪い設計です。
ネストされたクラスを作成する理由は 3 つあります。
- 組織化: クラスを別のクラスの名前空間に分類することが最も賢明な場合があります。特に、他のコンテキストで使用されない場合はそうです。
- アクセス: ネストされたクラスは、それらを含むクラスの変数/フィールドへの特別なアクセス権を持っています (正確には、どの変数/フィールドがネストされたクラスの種類 (内部か静的か) に依存します)。
- 利便性: 新しい型ごとに新しいファイルを作成する必要があるのは面倒です。特に、その型が 1 つのコンテキストでのみ使用される場合はなおさらです。
Java には4 種類のネストされたクラスがあります。簡単に言えば、それらは次のとおりです。
- static class : 別のクラスの静的メンバーとして宣言
- 内部クラス: 別のクラスのインスタンス メンバーとして宣言
- ローカル内部クラス: 別のクラスのインスタンス メソッド内で宣言
- 匿名内部クラス: ローカル内部クラスに似ていますが、1 回限りのオブジェクトを返す式として記述されます
もっと詳しく説明しましょう。
静的クラス
静的クラスは、それを含むクラスのインスタンスとは何の関係もないため、最も理解しやすい種類です。
静的クラスは、別のクラスの静的メンバーとして宣言されたクラスです。他の静的メンバーと同様に、そのようなクラスは実際には、含まれているクラスを名前空間として使用する単なるハンガーです。たとえば、パッケージPizzaでクラスRhinoの静的メンバーとして宣言されたクラスGoatは、 pizza.Rhino.Goatという名前で知られています。 .
package pizza;
public class Rhino {
...
public static class Goat {
...
}
}
率直に言って、クラスはすでにパッケージごとに名前空間に分割されているため、静的クラスはまったく価値のない機能です。静的クラスを作成する唯一の本当の考えられる理由は、そのようなクラスがそれを含むクラスのプライベート静的メンバーにアクセスできることですが、これは静的クラス機能が存在するためのかなり不十分な正当化であることがわかりました.
内部クラス
内部クラスは、別のクラスの非静的メンバーとして宣言されたクラスです。
package pizza;
public class Rhino {
public class Goat {
...
}
private void jerry() {
Goat g = new Goat();
}
}
静的クラスと同様に、内部クラスは、含まれるクラス名のPizza.Rhino.Goatによって修飾されたものとして認識されますが、含まれるクラス内では、単純な名前で認識されます。ただし、内部クラスのすべてのインスタンスは、それを含むクラスの特定のインスタンスに関連付けられています。上記のjerryで作成されたGoatは、暗黙的にRhinoインスタンスthis in jerryに関連付けられています。それ以外の場合は、 Goatをインスタンス化するときに、関連するRhinoインスタンスを明示的にします。
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(奇妙な新しい構文では、内側の型を単なるGoatと呼んでいることに注意してください。Java は、 rhinoの部分から含まれている型を推測します。そして、そうです、new rhino.Goat()は私にもより意味がありました。)
それで、これは私たちに何をもたらしますか?内部クラス インスタンスは、それを含むクラス インスタンスのインスタンス メンバーにアクセスできます。これらの外側のインスタンス メンバーは、 this ではなく単純な名前を介して内部クラス内で参照されます(内部クラスのthisは、関連する包含クラス インスタンスではなく、内部クラス インスタンスを参照します)。
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
内部クラスでは、包含クラスの this をRhino.thisとして参照できます。また、 thisを使用してそのメンバーを参照できます(例: Rhino.this.barry )。
ローカル内部クラス
ローカル内部クラスは、メソッドの本体で宣言されたクラスです。このようなクラスは、それを含むメソッド内でのみ認識されるため、インスタンス化して、そのメンバーを含むメソッド内でアクセスすることしかできません。利点は、ローカルの内部クラス インスタンスが結び付けられ、それを含むメソッドの最終的なローカル変数にアクセスできることです。インスタンスがそれを含むメソッドの最終的なローカルを使用する場合、変数がスコープ外に出たとしても、変数はインスタンスの作成時に保持していた値を保持します (これは事実上、Java の粗雑な限定バージョンのクロージャーです)。
ローカル内部クラスはクラスまたはパッケージのメンバーではないため、アクセス レベルで宣言されません。(ただし、そのメンバーには通常のクラスと同様のアクセス レベルがあることに注意してください。)
ローカル内部クラスがインスタンス メソッドで宣言されている場合、内部クラスのインスタンス化は、インスタンスの作成時に包含メソッドのthisによって保持されているインスタンスに結び付けられるため、包含クラスのインスタンス メンバーは、インスタンスのようにアクセスできます。内部クラス。ローカル内部クラスは、その名前によって単純にインスタンス化されます。たとえば、ローカル内部クラスCatは、予想されるように new this.Cat( ) ではなく、 new Cat()としてインスタンス化されます。
匿名内部クラス
匿名内部クラスは、ローカル内部クラスを記述する構文的に便利な方法です。最も一般的には、ローカル内部クラスは、それを含むメソッドが実行されるたびに 1 回だけインスタンス化されます。ローカルな内部クラスの定義とその単一のインスタンス化を 1 つの便利な構文形式に組み合わせることができればいいのですが、クラスの名前を考える必要がなければいいでしょう (役に立たないものは少なくなります)。コードに名前が含まれているほど良い)。匿名の内部クラスでは、次の両方が許可されます。
new *ParentClassName*(*constructorArgs*) {*members*}
これは、ParentClassNameを拡張する名前のないクラスの新しいインスタンスを返す式です。独自のコンストラクターを提供することはできません。むしろ、単にスーパー コンストラクターを呼び出すものが暗黙的に提供されるため、提供される引数はスーパー コンストラクターに適合する必要があります。(親に複数のコンストラクターが含まれている場合、「最も単純な」コンストラクターが呼び出されます。これは、詳細を学ぶ必要のないかなり複雑な一連の規則によって決定されるためです。NetBeans または Eclipse が教えてくれることに注意してください。)
または、実装するインターフェイスを指定できます。
new *InterfaceName*() {*members*}
このような宣言は、 Object を拡張してInterfaceNameを実装する名前のないクラスの新しいインスタンスを作成します。繰り返しになりますが、独自のコンストラクターを提供することはできません。この場合、Java は引数なしで何もしないコンストラクターを暗黙的に提供します (したがって、この場合、コンストラクター引数はありません)。
匿名の内部クラスにコンストラクターを与えることはできませんが、初期化ブロック (メソッドの外側に配置された {} ブロック) を使用して、必要な設定を行うことができます。
匿名の内部クラスは、1 つのインスタンスを持つローカルの内部クラスを作成する柔軟性の低い方法であることを明確にしてください。複数のインターフェースを実装するローカル内部クラス、またはオブジェクト以外のクラスを拡張しながらインターフェースを実装するローカル内部クラス、または独自のコンストラクターを指定するローカル内部クラスが必要な場合は、通常の名前付きローカル内部クラスの作成に行き詰まります。
上記の回答で本当の違いが明確になったとは思いません。
最初に条件を正しく理解するには:
- ネストされたクラスは、ソース コード レベルで別のクラスに含まれるクラスです。
- static修飾子で宣言するとstatic になります。
- 非静的ネスト クラスは内部クラスと呼ばれます。(私は静的ではないネストされたクラスにとどまります。)
マーティンの答えは今のところ正しいです。ただし、実際の問題は次のとおりです。ネストされたクラスを静的に宣言する目的は何ですか?
クラスがトピック的に一緒に属している場合、またはネストされたクラスが外側のクラスで排他的に使用されている場合にクラスをまとめておきたい場合は、静的なネストされたクラスを使用します。ネストされた静的クラスと他のすべてのクラスとの間に意味上の違いはありません。
非静的なネストされたクラスは別の獣です。匿名の内部クラスと同様に、そのようなネストされたクラスは実際にはクロージャーです。つまり、周囲のスコープとそれを囲むインスタンスをキャプチャし、アクセスできるようにします。おそらく、例がそれを明確にするでしょう。コンテナのこのスタブを参照してください。
public class Container {
public class Item{
Object data;
public Container getContainer(){
return Container.this;
}
public Item(Object data) {
super();
this.data = data;
}
}
public static Item create(Object data){
// does not compile since no instance of Container is available
return new Item(data);
}
public Item createSubItem(Object data){
// compiles, since 'this' Container is available
return new Item(data);
}
}
この場合、子アイテムから親コンテナへの参照が必要です。非静的なネストされたクラスを使用すると、これは何もしなくても機能します。Container の外側のインスタンスには、構文 でアクセスできますContainer.this
。
以下のよりハードコアな説明:
コンパイラーが (非静的) ネストされたクラスに対して生成する Java バイトコードを見ると、さらに明確になる可能性があります。
// class version 49.0 (49)
// access flags 33
public class Container$Item {
// compiled from: Container.java
// access flags 1
public INNERCLASS Container$Item Container Item
// access flags 0
Object data
// access flags 4112
final Container this$0
// access flags 1
public getContainer() : Container
L0
LINENUMBER 7 L0
ALOAD 0: this
GETFIELD Container$Item.this$0 : Container
ARETURN
L1
LOCALVARIABLE this Container$Item L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 1
public <init>(Container,Object) : void
L0
LINENUMBER 12 L0
ALOAD 0: this
ALOAD 1
PUTFIELD Container$Item.this$0 : Container
L1
LINENUMBER 10 L1
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
L2
LINENUMBER 11 L2
ALOAD 0: this
ALOAD 2: data
PUTFIELD Container$Item.data : Object
RETURN
L3
LOCALVARIABLE this Container$Item L0 L3 0
LOCALVARIABLE data Object L0 L3 2
MAXSTACK = 2
MAXLOCALS = 3
}
ご覧のとおり、コンパイラは隠しフィールドを作成しますContainer this$0
。これは、外側のインスタンスを指定するためのタイプ Container の追加パラメーターを持つコンストラクターで設定されます。このパラメーターはソースには表示されませんが、コンパイラーはネストされたクラスに対して暗黙的に生成します。
マーティンの例
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
(バイトコードで)のような呼び出しにコンパイルされます
new InnerClass(outerObject)
完全を期すために:
匿名クラスは、名前が関連付けられておらず、後で参照できない非静的なネストされたクラスの完璧な例です。
上記の回答のどれも、アプリケーション設計の観点から、ネストされたクラスと静的なネストされたクラスの本当の違いを説明していないと思います:
概要
ネストされたクラスは、非静的または静的である可能性があり、いずれの場合も別のクラス内で定義されたクラスです。ネストされたクラスは、エンクロージング クラスを提供するためだけに存在する必要があります。ネストされたクラスが他のクラス (エンクロージングだけでなく) によっても有用である場合は、最上位クラスとして宣言する必要があります。
違い
Nonstatic Nested class : 含まれているクラスの外側のインスタンスに暗黙的に関連付けられています。これは、外側のインスタンスのメソッドを呼び出して変数にアクセスできることを意味します。ネストされた非静的クラスの一般的な用途の 1 つは、Adapter クラスを定義することです。
Static Nested Class : 囲んでいるクラスのインスタンスにアクセスしてメソッドを呼び出すことはできないため、ネストされたクラスが囲んでいるクラスのインスタンスへのアクセスを必要としない場合に使用する必要があります。ネストされた静的クラスの一般的な用途は、外側のオブジェクトのコンポーネントを実装することです。
結論
したがって、設計の観点から見た 2 つの主な違いは次のとおりです。非静的ネスト クラスはコンテナー クラスのインスタンスにアクセスできますが、静的クラスはアクセスできません。
Java 内部クラスと静的ネスト クラスの主な相違点と類似点を次に示します。
それが役に立てば幸い!
内部クラス
- インスタンスと静的メソッドおよびフィールドの両方の外部クラスにアクセスできます
囲んでいるクラスのインスタンスに関連付けられているため、インスタンス化するには、最初に外部クラスのインスタンスが必要です (新しいキーワードの場所に注意してください)。
Outerclass.InnerClass innerObject = outerObject.new Innerclass();
静的メンバー自体を定義することはできません
- ClassまたはInterface宣言を持つことはできません
ネストされた静的クラス
外部クラスのインスタンスメソッドまたはフィールドにアクセスできません
囲んでいるクラスのインスタンスに関連付けられていないので、インスタンス化するには:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
類似点
- 両方の内部クラスは、外部クラスのプライベート フィールドとメソッドにもアクセスできます
- また、外部クラスは内部クラスのプライベート フィールドとメソッドにアクセスできます。
- どちらのクラスも、プライベート、保護、またはパブリック アクセス修飾子を持つことができます
ネストされたクラスを使用する理由
Oracle のドキュメントによると、いくつかの理由があります (完全なドキュメント):
これは、1 つの場所でのみ使用されるクラスを論理的にグループ化する方法です。あるクラスが他の 1 つのクラスだけに役立つ場合、そのクラスをそのクラスに埋め込んで 2 つをまとめておくのが論理的です。このような「ヘルパー クラス」をネストすると、パッケージがより合理化されます。
カプセル化が強化されます: 2 つの最上位クラス A と B を考えてみます。ここで、B は、そうでなければ非公開と宣言される A のメンバーにアクセスする必要があります。クラス A 内にクラス B を隠すことにより、A のメンバーを非公開と宣言し、B がそれらにアクセスできるようにします。さらに、B自体を外界から隠すことができます。
より読みやすく保守しやすいコードにつながる可能性があります。最上位クラス内に小さなクラスをネストすると、コードが使用される場所の近くに配置されます。
一般的に守られている慣習は次のとおりだと思います。
- トップレベルクラス内の静的クラスはネストされたクラスです
- 最上位クラス内の非静的クラスは内部クラスであり、さらに 2 つの形式があります。
- ローカル クラス- メソッドやコンストラクタ本体などのブロック内で宣言された名前付きクラス
- 匿名クラス- インスタンスが式およびステートメントで作成される名前のないクラス
ただし、覚えておくべき他のいくつかのポイントは次のとおりです。
トップレベル クラスと静的なネストされたクラスは意味的に同じですが、静的なネストされたクラスの場合は、その外側の [親] クラスのプライベートな静的フィールド/メソッドへの静的参照を行うことができ、その逆も可能です。
内部クラスは、外部 [親] クラスの外側のインスタンスのインスタンス変数にアクセスできます。ただし、すべての内部クラスに囲みインスタンスがあるわけではありません。たとえば、静的初期化子ブロックで使用される匿名クラスのように、静的コンテキストの内部クラスにはありません。
デフォルトでは、匿名クラスは親クラスを拡張するか、親インターフェースを実装します。他のクラスを拡張したり、インターフェースを実装したりする句はありません。そう、
new YourClass(){};
意味class [Anonymous] extends YourClass {}
new YourInterface(){};
意味class [Anonymous] implements YourInterface {}
どちらをいつ使用するかという大きな問題は未解決のままだと思います。それは主にあなたが扱っているシナリオに依存しますが、@jrudolph からの返信を読むと、決定を下すのに役立つ場合があります。
ネストされたクラス: クラス内のクラス
種類:
- ネストされた静的クラス
- 非静的ネスト クラス [内部クラス]
違い:
非静的ネスト クラス [内部クラス]
ネストされた非静的クラスでは、内部クラスのオブジェクトが外部クラスのオブジェクト内に存在します。外部クラスのデータ メンバーが内部クラスにアクセスできるようにします。したがって、内部クラスのオブジェクトを作成するには、最初に外部クラスのオブジェクトを作成する必要があります。
outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass();
ネストされた静的クラス
「静的」という言葉はオブジェクトを作成する必要がないことを示すため、内部クラスの静的ネストされたクラスオブジェクトは外部クラスのオブジェクトを必要としません。
class outerclass A {
static class nestedclass B {
static int x = 10;
}
}
x にアクセスする場合は、メソッド内に次のように記述します。
outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);
外部クラスのインスタンスが作成されると、内部クラスのインスタンスが作成されます。したがって、内部クラスのメンバーとメソッドは、外部クラスのインスタンス (オブジェクト) のメンバーとメソッドにアクセスできます。外部クラスのインスタンスがスコープ外になると、内部クラスのインスタンスも存在しなくなります。
ネストされた静的クラスには具体的なインスタンスがありません。初めて使用するときにロードされます (静的メソッドと同様)。これは完全に独立したエンティティであり、そのメソッドと変数は外部クラスのインスタンスにアクセスできません。
ネストされた静的クラスは外部オブジェクトと結合されず、高速であり、そのようなクラスのインスタンスを作成する必要がないため、ヒープ/スタック メモリを使用しません。したがって、経験則として、ネストされた静的クラスをできるだけ制限されたスコープ (private >= class >= protected >= public) で定義してから、それを内部クラスに変換し ("static" 識別子を削除して)、緩めます。本当に必要な場合はスコープ。
これらの用語は同じ意味で使用されます。それについて本当に衒学者になりたい場合は、「ネストされたクラス」を定義して、インスタンスを囲むことのない静的な内部クラスを参照することができます。コードでは、次のようなものがあります。
public class Outer {
public class Inner {}
public static class Nested {}
}
しかし、それは実際には広く受け入れられている定義ではありません。
インスタンス作成の場合、非静的内部クラスのインスタンスは、それが定義されている外部クラスのオブジェクトを参照して作成されます。これは、それが閉じているインスタンスを持っていることを意味します。ただし、静的内部クラスのインスタンスは、外部クラスのオブジェクトの参照ではなく、外部クラスの参照で作成されます。これは、インスタンスを囲んでいないことを意味します。
例えば:
class A
{
class B
{
// static int x; not allowed here…..
}
static class C
{
static int x; // allowed here
}
}
class Test
{
public static void main(String… str)
{
A o=new A();
A.B obj1 =o.new B();//need of inclosing instance
A.C obj2 =new A.C();
// not need of reference of object of outer class….
}
}
うーん...内部クラスはネストされたクラスです...匿名クラスと内部クラスを意味しますか?
編集:あなたが実際に内部対匿名を意味した場合...内部クラスは次のようなクラス内で定義された単なるクラスです:
public class A {
public class B {
}
}
匿名クラスは匿名で定義されたクラスの拡張であるため、次のように実際の「クラス」は定義されません。
public class A {
}
A anon = new A() { /* you could change behavior of A here */ };
さらに編集:
ウィキペディアはJavaに違いがあると主張していますが、私はJavaを8年間使用しており、そのような違いを聞いたのはこれが初めてです...言うまでもなく、主張を裏付ける参照はありません...下行では、内部クラスはクラス(静的または非静的)内で定義されたクラスであり、ネストされたものは同じことを意味する単なる別の用語です。
静的ネストクラスと非静的ネストクラスの間には微妙な違いがあります...基本的に非静的内部クラスは、インスタンスフィールドとそれを囲むクラスのメソッドに暗黙的にアクセスできます(したがって、静的コンテキストで構築することはできず、コンパイラになりますエラー)。一方、静的にネストされたクラスは、インスタンスのフィールドとメソッドに暗黙的にアクセスできず、静的なコンテキストで構築できます。
Java および/またはネストされたクラスの初心者である学習者を対象とする
ネストされたクラスは、次のいずれかになります。
1. 静的なネストされたクラス。
2. 非静的ネスト クラス。(内部クラスとも呼ばれます) =>これを覚えておいてください
1.内部クラス
例:
class OuterClass {
/* some code here...*/
class InnerClass { }
/* some code here...*/
}
内部クラスは、ネストされたクラスのサブセットです。
- 内部クラスは、ネストされたクラスの特定のタイプです
- 内部クラスはネストされたクラスのサブセットです
- 内部クラスもネストされたクラスであると言えますが、ネストされたクラスも内部クラスであるとは言えません。
インナークラスの特技:
- 内部クラスのインスタンスは、 「プライベート」とマークされているものも含め、外部クラスのすべてのメンバーにアクセスできます</li>
2.静的ネストされたクラス:
例:
class EnclosingClass {
static class Nested {
void someMethod() { System.out.println("hello SO"); }
}
}
ケース 1: 外側のクラスからネストされた静的クラスをインスタンス化する
class NonEnclosingClass {
public static void main(String[] args) {
/*instantiate the Nested class that is a static
member of the EnclosingClass class:
*/
EnclosingClass.Nested n = new EnclosingClass.Nested();
n.someMethod(); //prints out "hello"
}
}
ケース 2: 外側のクラスからネストされた静的クラスをインスタンス化する
class EnclosingClass {
static class Nested {
void anotherMethod() { System.out.println("hi again"); }
}
public static void main(String[] args) {
//access enclosed class:
Nested n = new Nested();
n.anotherMethod(); //prints out "hi again"
}
}
静的クラスの特徴:
- 静的内部クラスは、外部クラスの静的メンバーにのみアクセスでき、非静的メンバーにはアクセスできません。
結論:
質問: Java の内部クラスと静的ネスト クラスの主な違いは何ですか?
回答:上記の各クラスの詳細を確認してください。
ネストされたクラスは非常に一般的な用語です。トップレベルではないすべてのクラスはネストされたクラスです。内部クラスは、静的ではないネストされたクラスです。Joseph Darcy は、Nested、Inner、Member、および Top-Level Classについて非常に優れた説明を書きました。
クラス内で静的メンバー クラスを宣言する場合、それは最上位のネストされたクラスまたは静的なネストされたクラスとして知られています。以下のように示すことができます:
class Test{
private static int x = 1;
static class A{
private static int y = 2;
public static int getZ(){
return B.z+x;
}
}
static class B{
private static int z = 3;
public static int getY(){
return A.y;
}
}
}
class TestDemo{
public static void main(String[] args){
Test t = new Test();
System.out.println(Test.A.getZ());
System.out.println(Test.B.getY());
}
}
クラス内で非静的メンバー クラスを宣言すると、内部クラスと呼ばれます。内部クラスは以下のように示すことができます:
class Test{
private int i = 10;
class A{
private int i =20;
void display(){
int i = 30;
System.out.println(i);
System.out.println(this.i);
System.out.println(Test.this.i);
}
}
}
ここにいる人はポスターに次のことに気付くべきだと思います:静的ネストクラスは最初の内部クラスだけです。例えば:
public static class A {} //ERROR
public class A {
public class B {
public static class C {} //ERROR
}
}
public class A {
public static class B {} //COMPILE !!!
}
つまり、要約すると、静的クラスは、含まれるクラスに依存しません。したがって、彼らは通常のクラスではできません。(通常のクラスにはインスタンスが必要なため)。
ネストされたクラスのもう 1 つの使用例は、既に述べたものに加えて、ネストされたクラスに外部クラスからのみアクセスできるメソッドがある場合です。これが可能なのは、外側のクラスがネストされたクラスのプライベート コンストラクター、フィールド、およびメソッドにアクセスできるためです。
以下の例では、 はプライベート コンストラクターを持つBank
を発行し、 のプライベートインスタンス メソッドをBank.CreditCard
使用して、現在の銀行ポリシーに従ってクレジット カードの制限を変更できます。(この場合、インスタンス変数への直接フィールド アクセスも機能します)。他のクラスからは、のパブリック メソッドのみにアクセスできます。setLimit(...)
Bank.CreditCard
limit
Bank.CreditCard
public class Bank {
// maximum limit as per current bank policy
// is subject to change
private int maxLimit = 7000;
// ------- PUBLIC METHODS ---------
public CreditCard issueCard(
final String firstName,
final String lastName
) {
final String number = this.generateNumber();
final int expiryDate = this.generateExpiryDate();
final int CVV = this.generateCVV();
return new CreditCard(firstName, lastName, number, expiryDate, CVV);
}
public boolean setLimit(
final CreditCard creditCard,
final int limit
) {
if (limit <= this.maxLimit) { // check against current bank policy limit
creditCard.setLimit(limit); // access private method Bank.CreditCard.setLimit(int)
return true;
}
return false;
}
// ------- PRIVATE METHODS ---------
private String generateNumber() {
return "1234-5678-9101-1123"; // the numbers should be unique for each card
}
private int generateExpiryDate() {
return 202405; // date is YYYY=2024, MM=05
}
private int generateCVV() {
return 123; // is in real-life less predictable
}
// ------- PUBLIC STATIC NESTED CLASS ---------
public static final class CreditCard {
private final String firstName;
private final String lastName;
private final String number;
private final int expiryDate;
private final int CVV;
private int balance;
private int limit = 100; // default limit
// the constructor is final but is accessible from outer class
private CreditCard(
final String firstName,
final String lastName,
final String number,
final int expiryDate,
final int CVV
) {
this.firstName = firstName;
this.lastName = lastName;
this.number = number;
this.expiryDate = expiryDate;
this.CVV = CVV;
}
// ------- PUBLIC METHODS ---------
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getNumber() {
return this.number;
}
public int getExpiryDate() {
return this.expiryDate;
}
// returns true if financial transaction is successful
// otherwise false
public boolean charge(final int amount) {
final int newBalance = this.balance - amount;
if (newBalance < -this.limit) {
return false;
}
this.balance = newBalance;
return true;
}
// ------- PRIVATE METHODS ---------
private int getCVV() {
return this.CVV;
}
private int getBalance() {
return this.balance;
}
private void setBalance(final int balance) {
this.balance = balance;
}
private int getLimit() {
return limit;
}
private void setLimit(final int limit) {
this.limit = limit;
}
}
}