多くの人は Java の内部クラスに馴染みがないかもしれませんが、C++ にも同様の概念、つまり入れ子クラスが存在します。以下では、この 2 つの違いと関係を比較します。表面的には、内部クラスはクラス内で定義された別のクラスにすぎません (以下でわかるように、内部クラスはさまざまな場所で定義できます)。しかし、実際には、一見したところ、内部クラスはそれほど単純ではありません。初心者にとってはその有用性はそれほど明らかではないかもしれませんが、より深く理解すると、Java の設計者が内部クラスに善意を持っていることがわかります。内部クラスの使用方法を学ぶことは、高度な Java プログラミングを習得する一環であり、これによりプログラム構造をよりエレガントに設計できるようになります。以下の観点から紹介していきます。
初対面
次のようにコードをコピーします。
パブリック インターフェイス コンテンツ {
int 値();
}
パブリック インターフェイスの宛先 {
文字列読み取りラベル();
}
パブリッククラスグッズ{
プライベート クラス Content は、Contents {を実装します。
プライベート int i = 11;
public int value() {
私を返します。
}
}
保護されたクラス GDestination は Destination { を実装します。
プライベート文字列ラベル。
private GDestination(String whereTo) {
ラベル = どこへ;
}
public String readLabel() {
ラベルを返します。
}
}
public Destination dest(String s) {
新しい GDestination を返します。
}
パブリックコンテンツ cont() {
新しい Content() を返します。
}
}
クラス TestGoods {
public static void main(String[] args) {
商品 p = 新しい商品();
内容 c = p.cont();
目的地 d = p.dest("北京");
}
}
この例では、Content クラスと GDestination クラスが Goods クラス内で定義されており、それぞれアクセス レベルを制御するための protected 修飾子と private 修飾子があります。 Content は商品の内容を表し、GDestination は商品の目的地を表します。これらは、Content と Destination という 2 つのインターフェイスをそれぞれ実装します。次の main メソッドでは、Contents c と Destination d を直接使用して操作します。これら 2 つの内部クラスの名前さえ表示されません。このように、内部クラスの最初の利点は、他人に知られたくない操作、つまりカプセル化を隠すことです。
同時に、外部クラスのスコープ外で内部クラスのオブジェクトを取得する最初の方法も発見しました。それは、外部クラスのメソッドを使用してオブジェクトを作成して返すことです。上記の例の cont() メソッドと dest() メソッドはこれを行います。それで、他の方法はありますか?
もちろんそれはあります。
構文形式は次のとおりです。
externalObject=新しいouterClass(コンストラクターパラメータ);
innerClass.innerClass innerObject=outerObject.new InnerClass(コンストラクター パラメーター);
非静的内部クラス オブジェクトを作成する場合は、まず対応する外部クラス オブジェクトを作成する必要があることに注意してください。その理由については、次のトピックにつながります。非静的内部クラス オブジェクトは、その外部クラス オブジェクトへの参照を持ちます。
前の例を少し変更します。
次のようにコードをコピーします。
パブリッククラスグッズ{
プライベート int valueRate = 2;
プライベート クラス Content は、Contents {を実装します。
private int i = 11 * valueRate;
public int value() {
私を返します。
}
}
保護されたクラス GDestination は Destination { を実装します。
プライベート文字列ラベル。
private GDestination(String whereTo) {
ラベル = どこへ;
}
public String readLabel() {
ラベルを返します。
}
}
public Destination dest(String s) {
新しい GDestination を返します。
}
パブリックコンテンツ cont() {
新しい Content() を返します。
}
}
ここでは、プライベート メンバー変数 valueRate を Goods クラスに追加します。これは、内部クラス Content の value() メソッドが値を計算するときに、商品の値係数が乗算されることを意味します。 value() は valueRate にアクセスできることがわかりました。これは、内部クラスの 2 番目の利点です。内部クラス オブジェクトは、それを作成した外部クラス オブジェクトの内容 (プライベート変数にも) にアクセスできます。これは、設計時により多くのアイデアやショートカットを提供してくれる非常に便利な機能です。この機能を実現するには、内部クラス オブジェクトに外部クラス オブジェクトへの参照が必要です。 Java コンパイラは、内部クラス オブジェクトを作成すると、その参照を暗黙的に外部クラス オブジェクトに渡し、保持します。これにより、内部クラス オブジェクトは常にその外部クラス オブジェクトにアクセスできるようになります。また、これが、外部クラスのスコープ外に内部クラス オブジェクトを作成する場合、最初にその外部クラス オブジェクトを作成する必要がある理由でもあります。
内部クラスのメンバ変数が外部クラスのメンバ変数と同じ名前である場合、つまり、同じ名前の外部クラスのメンバ変数がブロックされている場合はどうすればよいのかと疑問に思う人もいるかもしれません。 Java では、次の形式を使用して外部クラスへの参照を表現します。
外部クラス.this
これがあれば、この遮蔽状況を恐れることはありません。
静的内部クラス
通常のクラスと同様に、内部クラスも静的にすることができます。ただし、非静的内部クラスと比較すると、静的内部クラスには外部参照がないという違いがあります。これは実際には C++ の入れ子クラスに非常に似ています。Java の内部クラスと C++ の入れ子クラスの最大の違いは、もちろん、設計の観点とその詳細の一部に違いがあります。
さらに、非静的内部クラスには、静的データ、静的メソッド、または別の静的内部クラスを含めることはできません (内部クラスは複数のレベルでネストできます)。ただし、これらすべてを静的内部クラスで持つことができます。これが両者の 2 番目の違いと考えられます。
ローカル内部クラス
はい、Java 内部クラスはローカルにすることもでき、メソッド内またはコード ブロック内で定義することもできます。
次のようにコードをコピーします。
パブリッククラス Goods1 {
public Destination dest(String s) {
クラス GDestination は Destination {を実装します。
プライベート文字列ラベル。
private GDestination(String whereTo) {
ラベル = どこへ;
}
public String readLabel() {
ラベルを返します。
}
}
新しい GDestination を返します。
}
public static void main(String[] args) {
Goods1 g = 新しい Goods1();
目的地 d = g.dest("北京");
}
}
上記はその一例です。 dest メソッドでは内部クラスを定義し、最後にこのメソッドはこの内部クラスのオブジェクトを返します。内部クラスのオブジェクトを作成し、それを外部に作成するだけであれば、これを行うことができます。もちろん、メソッド内に定義された内部クラスは設計を多様化することができ、用途はこれに限定されるものではありません。
さらに奇妙な例を次に示します。
次のようにコードをコピーします。
パブリッククラス Goods2 {
private void externalTracking(boolean b) {
if (b) {
クラス TrackingSlip {
プライベート文字列ID;
TrackingSlip(String s) {
id = s;
}
文字列 getSlip() {
ID を返します。
}
}
TrackingSlip ts = new TrackingSlip("スリップ");
文字列 s = ts.getSlip();
}
}
public void track() {
内部追跡(true);
}
public static void main(String[] args) {
Goods2 g = 新しい Goods2();
g.track();
}
}
この内部クラスのオブジェクトは if のスコープを超えているため、if の外に作成することはできません。ただし、内部クラス TrackingSlip は、コンパイル中に他のクラスと同時にコンパイルされます。ただし、独自のスコープがあり、このスコープを越えると無効になる点が異なります。それ以外は、他の内部クラスと変わりません。
匿名内部クラス
Java の匿名内部クラスの構文規則は少し奇妙に思えるかもしれませんが、匿名配列と同様、クラスのオブジェクトを作成するだけでよく、名前が必要ない場合は、内部クラスを使用するとコードを簡潔かつ明確に見せることができます。その構文規則は次のとおりです。
新しいインターフェース名(){......}; または新しいスーパークラス名(){....};
以下の例を続けてみましょう。
次のようにコードをコピーします。
パブリッククラス Goods3 {
パブリックコンテンツ cont() {
新しいコンテンツを返す() {
プライベート int i = 11;
public int value() {
私を返します。
}
};
}
}
ここでのメソッド cont() は、匿名の内部クラスを使用して、Contents インターフェイスを実装するクラスのオブジェクトを直接返します。実際、非常に単純に見えます。
Java イベント処理用の匿名アダプターでは、匿名内部クラスが広く使用されています。たとえば、ウィンドウを閉じたい場合は、次のコードを追加します。
次のようにコードをコピーします。
Frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
注意すべき点の 1 つは、匿名内部クラスには名前がないため、コンストラクターもありません (ただし、匿名内部クラスがパラメーター化されたコンストラクターのみを含む親クラスを継承する場合は、作成時にこれらのパラメーターを取得する必要があり、スーパー クラスを使用する必要があります)。実装プロセス中に対応するコンテンツを呼び出すためのキーワード)。メンバー変数を初期化したい場合は、いくつかの方法があります。
メソッドの匿名内部クラス内にある場合は、このメソッドを使用して必要なパラメータを渡すことができますが、これらのパラメータはfinalとして宣言する必要があることに注意してください。
コンストラクターを持てるように、匿名内部クラスを名前付きローカル内部クラスに変換します。
この匿名内部クラスで初期化ブロックを使用します。
なぜ内部クラスが必要なのでしょうか?
Java 内部クラスの利点は何ですか?なぜ内部クラスが必要なのでしょうか?
まず、簡単な例を考えてみましょう。インターフェイスを実装したいが、このインターフェイスのメソッドの名前とパラメータが、考えたクラスのメソッドと同じである場合、どうすればよいでしょうか。現時点では、このインターフェイスを実装するための内部クラスを構築できます。内部クラスは外部クラスのすべてにアクセスできるため、これにより、インターフェイスを直接実装した場合に得られるすべての機能が実現されます。
しかし、方法を変えるだけで十分ではないのではないか、と疑問に思うかもしれません。
確かに、これを内部クラスを設計する理由として使用するのは、まったく説得力がありません。
本当の理由は、Java の内部クラスとインターフェイスを組み合わせることで、Java で C++ プログラマがよく不満を抱く問題を多重継承なしで解決できるということです。実際、C++ の多重継承の設計は非常に複雑ですが、Java は内部クラスとインターフェイスを通じて多重継承の効果を非常にうまく実現できます。