JAVA内部クラスに関する知識


Java内部クラスまとめ
内部クラスとは、1つの外部クラスの内部でもう1つのクラスを定義することです.内部クラスは外部クラスのメンバーとして存在し、外部クラスに依存して存在する.内部クラスは静的であり、protectedおよびprivateで修飾できます(外部クラスではpublicおよびデフォルトのパケットアクセスのみが使用できます).内部クラスには主に以下のクラスがあります.メンバー内部クラス、ローカル内部クラス、静的内部クラス、匿名内部クラスはなぜ内部クラスが必要ですか.典型的には、内部クラスはクラスから継承されるか、インタフェースを実装し、内部クラスのコード操作はその周辺クラスのオブジェクトを作成します.内部クラスは、その周辺クラスに入るウィンドウを提供していると考えられます.内部クラスを使用する最も魅力的な理由は、各内部クラスが独立して1つの(インタフェースの)実装から継承できるため、周辺クラスが何らかの(インタフェースの)実装を継承しているかどうかにかかわらず、内部クラスには影響しません.内部クラスが複数の具体的または抽象的なクラスを継承できる能力を提供していない場合、いくつかの設計とプログラミングの問題は解決しにくい.この観点から,内部クラスは多重継承ソリューションを完全にする.インタフェースは一部の問題を解決し、内部クラスは効果的に「多重継承」を実現します.A:メンバー内部クラスは外部クラスのメンバーとして存在し、外部クラスの属性、メソッドと並んでいる.
publicclass Outer {
privatestaticinti = 1;
privateintj = 10;
privateintk = 20;
publicstaticvoidouter_f1() {
}
publicvoidouter_f2() {
}
//       ,        
//       ,            
class Inner {
// static int inner_i = 100; //             
intj = 100; //                 
intinner_i = 1;
void inner_f1() {
System.out.println(i);
//                     
System.out.println(j);
//                   this.   
System.out.println(this.j);
//                           .this.   
System.out.println(Outer.this.j);
//                 ,                
System.out.println(k);
outer_f1();
outer_f2();
}
}
//                
publicvoidouter_f3() {
Inner inner = new Inner();
inner.inner_f1();
}
//                ,                
publicstaticvoidouter_f4() {
//step1        
Outer out = new Outer();
//step2               
Inner inner = out.new Inner();
//step3         
inner.inner_f1();
}
publicstaticvoid main(String[] args) {
//outer_f4(); //                      
//             ,               Outer   ,
//                  ,                
//           :
//Outer.Inner outin = out.new Inner()
//  ,               ,             。   
//                       。            ,
//               。
Outer out = new Outer();
Outer.Inner outin = out.new Inner();
outin.inner_f1();
}
}

注意:内部クラスはコンパイル時の概念であり、コンパイルに成功すると、まったく異なる2つのクラスになります.outerという外部クラスとその内部定義のinnerという内部クラスについて.コンパイルが完了するとouterが表示されます.classとouter$inner.classの2種類.B:ローカル内部クラスメソッドで定義された内部クラスをローカル内部クラスと呼びます.ローカル変数と同様に、ローカル内部クラスには、周辺クラスの一部ではないためアクセス説明子は使用できませんが、現在のコードブロック内の定数と、この周辺クラスのすべてのメンバーにアクセスできます.
publicclass Outer {
privateints = 100;
privateintout_i = 1;
publicvoid f(finalint k) {
finalint s = 200;
int i = 1;
finalint j = 10;
//       
class Inner {
ints = 300; //              
// static int m = 20; //         
Inner(int k) {
inner_f(k);
}
intinner_i = 100;
voidinner_f(int k) {
//                ,                   
System.out.println(out_i);
//            (       ),       final 
System.out.println(j);
//System.out.println(i);
//                ,                
System.out.println(s);
// this.             
System.out.println(this.s);
//     .this.               
System.out.println(Outer.this.s);
}
}
new Inner(k);
}
publicstaticvoid main(String[] args) {
//                 
Outer out = new Outer();
out.f(3);
}
}

C:静的内部クラス(ネストされたクラス):(注意:最初の2つの内部クラスは変数と似ているので、参照変数と照合できます)内部クラスオブジェクトとその周辺クラスオブジェクトとのつながりを必要としない場合は、内部クラスをstaticとして宣言できます.これは通常、ネストクラス(nested class)と呼ばれます.staticが内部クラスに適用される場合の意味を理解するには、通常の内部クラスオブジェクトには、その周辺クラスオブジェクトを作成する参照が暗黙的に保存されていることを覚えておく必要があります.しかし,内部クラスがstaticである場合,そうではない.ネストされたクラスは、1を意味します.ネストされたクラスのオブジェクトを作成するには、その周辺クラスのオブジェクトは必要ありません.  2.ネストされたクラスのオブジェクトから非静的な周辺クラスオブジェクトにアクセスできません.
publicclass Outer {
privatestaticinti = 1;
privateintj = 10;
publicstaticvoidouter_f1() {
}
publicvoidouter_f2() {
}
//         public,protected,private  
//                     
staticclass Inner {
staticintinner_i = 100;
intinner_j = 200;
staticvoidinner_f1() {
//                 (           )
System.out.println("Outer.i" + i);
outer_f1();
}
voidinner_f2() {
//                   (             )
// System.out.println("Outer.i"+j);
// outer_f2();
}
}
publicvoidouter_f3() {
//              :   .    
System.out.println(Inner.inner_i);
Inner.inner_f1();
//               :        
Inner inner = new Inner();
inner.inner_f2();
}
publicstaticvoid main(String[] args) {
newOuter().outer_f3();
}
}

静的内部クラスを生成するには、外部クラスメンバーは必要ありません.これは静的内部クラスとメンバー内部クラスの違いです.静的内部クラスのオブジェクトは直接生成することができる:Outer.Inner in = new Outer.Inner();外部クラスオブジェクトを生成することによって生成する必要はありません.これにより、実際には静的内部クラスがトップクラスになります(通常、インタフェースの内部にコードを配置することはできませんが、ネストされたクラスはstaticであるため、インタフェースの一部として使用できます.ネストされたクラスをインタフェースのネーミングスペースに配置するだけで、インタフェースのルールに違反しません).D:匿名内部クラス(from thinking in java 3 th)簡単に言えば、匿名の内部クラスは名前のない内部クラスです.匿名の内部クラスを使用する必要があるのはどのような場合ですか?匿名の内部クラスを使用するのは、以下の条件を満たす場合に適しています.・クラスのインスタンスの1つだけ使用します.・クラスは定義後すぐに使用する.・クラスが非常に小さい(SUN推奨は4行以下)・クラスに名前を付けるとコードが理解されやすくなりません.匿名内部クラスを使用する場合は、・匿名内部クラスは構築方法がないという原則を覚えておきましょう.・匿名内部クラスでは、静的メンバー、メソッド、クラスは定義できません.・匿名内部クラスはpublic,protected,private,staticではいけません.・匿名内部クラスのインスタンスは1つしか作成できません.・匿名の内部クラスは、newの後ろにあり、インタフェースを暗黙的に実装したり、クラスを実装したりするに違いない.・匿名内部クラスはローカル内部クラスであるため、ローカル内部クラスのすべての制限が有効となる.次の例は少し変に見えます.
//             
public class Parcel6 {
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() {
return i;
}
}; //          
}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
Contents c = p.cont();
}
}

cont()メソッドは、次の2つのアクションを結合します.戻り値の生成と、この戻り値を表すクラスの定義です.さらに言えば、このクラスは匿名で、名前はありません.さらに悪いことに、Contentsオブジェクト:return new Contents()を作成しようとしているように見えますが、文の終わりのセミコロンに達する前に、「ちょっと待って、ここにクラスの定義を挿入したい」と言います.return new Contents(){private int i=11;public int value(){return i; }};この奇妙な構文は、「Contentsから継承された匿名クラスのオブジェクトを作成する」ことを意味します.new式で返される参照は、Contentsへの参照に自動的に変換されます.匿名内部クラスの構文は、class MyContents implements Contents{private int i=11;public int value(){return i; }}return new MyContents();この匿名の内部クラスでは、デフォルトのコンストラクタを使用してContentsを生成します.次のコードは、ベースクラスにパラメータのあるコンストラクタが必要な場合、どうすればいいかを示しています.
public class Parcel7 {
public Wrapping wrap(int x) {
// Base constructor call:
return new Wrapping(x) { // Pass constructor argument.
public int value() {
return super.value() * 47;
}
}; // Semicolon required
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Wrapping w = p.wrap(10);
}
}

適切なパラメータをベースクラスのコンストラクタに簡単に渡すだけでよいが,ここではxをnew Wrapping(x)に伝達する.匿名の内部クラスの末尾にあるセミコロンは、この内部クラスの終了をマークするために使用されるものではありません(C++ではそうです).実際には、式の終わりをマークしていますが、この式にはちょうど内部クラスが含まれています.そのため、これは他の場所で使われているセミコロンと一致しています.匿名クラスでメンバー変数を定義すると、同じように初期化操作を実行できます.
public class Parcel8 {
// Argument must be final to use inside
// anonymous inner class:
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Destination d = p.dest("Tanzania");
}
}

匿名の内部クラスがある場合、外部で定義されたオブジェクトを使用すると、コンパイラはdest()のパラメータのようにパラメータ参照がfinal型であることを要求します.忘れた場合は、コンパイル期間のエラーメッセージが表示されます.単純にメンバー変数に値を割り当てるだけであれば、この例の方法でよい.しかし、コンストラクタのような行為をしたい場合は、どうすればいいのでしょうか.匿名クラスに名前の付いたコンストラクタがあるはずがありません(名前がないからです!)、しかし、インスタンスの初期化により、匿名の内部クラスのコンストラクタを「作成」する効果が得られます.このようにします.
abstract class Base {
public Base(int i) {
System.out.println("Base constructor, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
{
System.out.println("Inside instance initializer");
}
public void f() {
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}

この例では、変数iが必ずfinalであることは要求されない.iは匿名クラスのベースクラスのコンストラクタに渡されるため,匿名クラス内部で直接使用されるわけではない.次の例は、インスタンス初期化付きの「parcel」形式です.注意dest()のパラメータは、匿名クラスで使用されるためfinalでなければなりません.
public class Parcel9 {
public Destinationdest(final String dest, final float price) {
return new Destination() {
private int cost;
// Instance initialization for each object:
{
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!");
}
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.dest("Tanzania", 101.395F);
}
}

インスタンスの初期化の部分には、メンバー変数の初期化の一部として実行できないコード(if文)が表示されます.したがって、匿名クラスでは、インスタンスの初期化の実際の効果はコンストラクタです.もちろん制限されています.インスタンスを再ロードして初期化することはできません.そのため、コンストラクタは1つしかありません.多層ネストされたクラスから外部の内部クラスがネストされている階層にアクセスすることは重要ではありません.以下に示すように、埋め込まれているすべての周辺クラスのすべてのメンバーに透過的にアクセスできます.
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}

MNAに見えます.A.Bでは、呼び出し方法g()およびf()は、privateとして定義されている場合でも、条件を必要としない.この例では、異なるクラスから多層ネストされた内部クラスオブジェクトを作成する方法の基本構文も示します.「.new」構文は正しい役割ドメインを生成するので、コンストラクタを呼び出すときにクラス名を限定する必要はありません.内部クラスのリロードの問題内部クラスを作成し、その周辺クラスを継承して内部クラスを再定義した場合、どうなりますか?つまり、内部クラスはリロードできますか?これは役に立つアイデアのように見えますが、内部クラスをリロードすることは、周辺クラスの1つの方法のように、実際には何の役にも立ちません.
class Egg {
private Yolk y;
protectedclass Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}
publicclass BigEgg extends Egg {
publicclass Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
publicstaticvoid main(String[] args) {
new BigEgg();
}
}

出力結果は、New Egg()Egg.Yolk()デフォルトのコンストラクタは、コンパイラによって自動的に生成されます.ここでは、ベースクラスを呼び出すデフォルトのコンストラクタです.BigEggのオブジェクトを作成した以上、「リロード」されたYolkを使用するべきだと思いますが、出力から実際の状況はそうではありません.この例は、ある周辺クラスを継承したとき、内部クラスに特に不思議な変化はなかったことを示しています.この2つの内部クラスは完全に独立した2つのエンティティであり、それぞれが自分のネーミングスペース内にあります.もちろん、内部クラスを明確に継承することもできます.
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}

出力結果:Egg 2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f()現在BigEgg 2.Yolkはextends Egg 2を通過する.Yolkはこの内部クラスを明確に継承し,その中のメソッドを再ロードした.Egg 2のinsertYolk()メソッドは、BigEgg 2が独自のYolkオブジェクトを上に変換し、参照yに渡すようにします.したがって、g()がy.f()を呼び出すと、リロード後の新版のf()が実行される.2回目の呼び出しEgg 2.Yolk()はBigEgg 2である.Yolkのコンストラクタは、そのベースクラスのコンストラクタを呼び出します.g()を呼び出すと,新版のf()が呼び出されるのがわかる.内部クラスの継承問題(thinking in java 3 th p 294)内部クラスのコンストラクタはその外周クラスオブジェクトの参照を使用するため、内部クラスを継承する際に少し複雑になります.問題は、その「秘密の」周辺クラスオブジェクトの参照を初期化する必要があり、継承されたクラスには結合するデフォルトオブジェクトが存在しないことです.この問題を解決するには、専門的な文法を使用して、それらの関連を明確に説明する必要があります.
class WithInner {
class Inner {
Inner(){
System.out.println("this is a constructor in WithInner.Inner");
};
}
}
public class InheritInner extends WithInner.Inner {
// ! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
System.out.println("this is a constructor in InheritInner");
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}

出力結果:this a constructor in WithInner.Innerthis a constructor in InheritInnerには、InheritInnerは周辺クラスではなく内部クラスからのみ継承されていることがわかります.しかし、コンストラクタを生成する場合、デフォルトのコンストラクタはよくありません.また、周辺クラスオブジェクトへの参照を渡すだけではありません.また、コンストラクタ内で次の構文を使用する必要があります.enclosingClassReference.super();これにより、必要な参照が提供され、プログラムがコンパイルされます.