これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります
前回の「“ネスト”した型で始める軽量Javaプログラミング!?」では、通常のJavaプログラミングよりもライトウェイト(軽量)なプログラミングをするために、「ネストした型/クラスとは何か」について基本的な説明をしました。
ネストした型をきちんと利用できるようになるには、「staticのネストしたクラス」「内部クラス(インナークラス)」「エンクロージング型へのアクセス」「内部クラスのextends」「隠蔽」「無名内部クラス(匿名クラス)」「ローカル内部クラス」といったことにも、理解が必要です。今回は、ネストした型について、さらに深く学びましょう。
ネストした型については、次のようにstatic修飾子が付くかどうかで、特徴が変わります。ネストしたインターフェイスは、常にstaticなので、「staticのネストした型」に含まれます。さらに、内部クラスは、宣言をする場所、宣言方法によって、特長が変わります。便宜上、次の3つに分けて理解しておくといいでしょう。
前回はネストした型について理解をするために、内部クラスのメンバ・クラスについて説明しました。また、ネストしたインターフェイスについても説明をしました。今回は、それ以外の4つのネストした型の文法・使い方を中心に説明します。
EclipseでJavaプログラミングを始める準備がまだの方は、連載第1回の「Eclipse 3.4で超簡単Javaプログラミング基礎入門」で準備をしておいてください。
ネストしたクラスに「static」を付けると、「staticのネストしたクラス」になります。ネストしたインターフェイスについては、前回説明しましたが、「staticを付けなくても暗黙のうちにstaticとなる」ので、staticを付けても付けなくても、常にstaticとなります。
インターフェイスについては、普通の開発では通常のインターフェイスとして宣言する方が自然でしょうが、ライトウェイトプログラミングをしているときは、ファイルを別にしたくないこともあります。そんなときに利用するといいでしょう。
実は、インターフェイス内に「ネストしたインターフェイス」を宣言することもできます。
どんな場合に使うかというと、インターフェイス内のメソッドで使用する型を宣言するに当たり、汎用性のある型として宣言したいときにネストしたインターフェイスを使うことが考えられます。利用する場面はそれほどありませんが、知識として覚えておくとよいでしょう。
これらは、エンクロージング型のstaticメンバーとなり、振る舞いとしては「トップレベル」のクラスやインターフェイス(ネストしていないクラスやインターフェイス)と同じです。
ただし、名前については「エンクロージング型名.ネストした型名」となり、ネストした型へのアクセスは、エンクロージング型(ネストした型を囲む型)にアクセスできる場合に、可能となります。
それでは、staticのネストしたクラスについて調べるプログラムを作成してみましょう。「sample17」というパッケージを作成して、次に示すSample1クラスを作成してみましょう。
package sample17; public class Sample1 { private static int v1 = 1; private int v2 = 2; public int v3 = 3; static class Ic { public static int vi1 = 11; public int vi2 = 12; public void print() { System.out.println("v1 :" + v1); System.out.println("vi2 :" + vi2); //System.out.println("v2 :" + v2); // エラー } public void printSample1(Sample1 o) { System.out.println("o.v2:" + o.v2); } } }
Sample1クラス内には、private staticなフィールドとして「v1」、privateなフィールドとして「v2」、publicなフィールドとして「v3」を用意しています。また、staticのネストしたクラスとしてSample1.Icクラスを宣言しています。
Sample1.Icクラスでは、public static なフィールドとして「vi1」、public なフィールドとして「vi2」を用意しています。メソッドとしては、printメソッドとprintSample1メソッドを用意しています。
ここで注目してほしいのは、ネストしたクラスIcのprintメソッド内で、トップレベルのクラスSample1のprivate staticなフィールドであるv1にアクセスできている点です。
また、privateなフィールドであるv2は、Sample1のオブジェクト経由でないとアクセスできませんから、「v2」と書いただけではアクセスできません。しかし、IcクラスのprintSample1メソッドの中で書いてあるように、メソッドのパラメータとしてSample1のオブジェクトを受け取った場合には、受け取ったオブジェクトのv2フィールドへアクセスできます。
次に、これを利用するsample17.Sample1Appクラスを次のように作成します。
package sample17; public class Sample1App { public static void printSample1(Sample1 o) { //System.out.println("o.v2:" + o.v2); // エラー System.out.println("v3 :" + o.v3); System.out.println("vi1 :" + Sample1.Ic.vi1); } public static void main(String[] args) { Sample1.Ic ic = new Sample1.Ic(); ic.print(); Sample1 enclosingClassObject = new Sample1(); ic.printSample1(enclosingClassObject); Sample1App.printSample1(enclosingClassObject); } }
こちらにも、printSample1という名のメソッドを用意しています。Sample1.IcのprintSample1メソッドでアクセスできたSample1オブジェクトのv2フィールドが、こちらではアクセスできません。privateなフィールドですから、Sample1Appクラスのオブジェクトからはアクセスできないのです。
一方、Sample1クラスのpublicなフィールドであるv3はアクセスできます。また、Sample1.Icクラスのpublic staticなフィールドであるvi1もアクセスできます。
mainメソッドでは、インスタンス生成の方法について注目してください。Sample1.Icクラスのインスタンスはnew演算子を使って、通常のクラスのインスタンス生成と同じようにできます。mainメソッド内は見ての通り、単にSample1.Icクラスのprintメソッド、printSample1メソッドを呼んでから、Sample1AppクラスのprintSample1メソッドを呼んでいるだけです。
以下は、Sample1App.javaの実行結果です。
v1 :1 vi2 :12 o.v2:2 v3 :3 vi1 :11
staticのネストしたクラスは、外部のクラスからインスタンス化することもでき、エンクロージング型のprivateフィールドへもアクセスできるので、エンクロージング型の実装を意識してプログラミング可能です。
あるクラスAと関係の深いクラスBを作成するに当たり、クラスBを同一パッケージのクラスとすることによるアクセス制御では十分でない場合、クラスBをstaticのネストしたクラスとして用意することを検討する価値があります。
例えばプログラムの再利用性を考える場合、実装を隠ぺいした方がいいのですが、実行速度や処理効率などを要求される場合は対応が難しいこともあります。配列を使っているとか、java.util.LinkedListを使っているとか、そういった実装を意識したプログラミングが必要になる場面があります。
ライトウェイトプログラミング中の場合、エンクロージング型と完全に別なクラスにするか、ネストしたクラスにするか、悩むクラスについては、手軽にトップレベル相当のクラスとして扱えて便利な「staticなネストしたクラス」として宣言しておけばいいでしょう。
staticではないネストしたクラスは、「内部クラス(inner class)」と呼ばれます。内部クラスのインスタンスは、「エンクロージングインスタンス(内部クラスを囲む型のインスタンス)」と関連付けされますから、エンクロージングインスタンスがないと、内部クラスのインスタンスは生成できません。
staticのネストしたクラスは、トップレベルのクラスとほとんど同じように利用できるのに対し、内部クラスはエンクロージング型のインスタンスに関連付ける必要があります。このことから、エンクロージング型のインスタンス変数にアクセスする積極的な理由があるなら、ネストしたクラスは内部クラスとするのがいいですし、そうでないならstaticのネストしたクラスにしておくのがいいでしょう。
使いどころとしては、ほかにも以下のような例があります。
次ページで、内部クラスにおけるエンクロージングインスタンスへのアクセスやextendsについてます。
Copyright © ITmedia, Inc. All Rights Reserved.