クラスは、メソッドとフィールドを持つことができます。さらに、内部クラスというものを持つことができます。いろいろな用途があって内部クラスを利用する場合があるのですが、本節では以下の 2つの場合に絞って解説を進めていきます。
定数を格納するために専用のクラスを用いる場合、定数のグループ分けをするために内部クラスを使用することがあります。サンプルプログラムを挙げます。
なお、この場合、内部クラスの修飾子はpublic staticになることが多いです。
ソースコードは以下の通り。
Y201/Setting.java
/** * 設定を格納するクラスです。 */ public class Setting { /** * モードA。 */ public static class ModeA { /** * 速度。 */ public static final int SPEED = 100; /** * 位置。 */ public static final int POSITION = 50; } /** * モードB。 */ public static class ModeB { /** * 速度。 */ public static final int SPEED = 150; } }
Y201/Y201.java
/** * 内部クラスを利用します。 */ public class Y201 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { System.out.println("ModeAの速度の値は " + Setting.ModeA.SPEED); System.out.println("ModeAの位置の値は " + Setting.ModeA.POSITION); System.out.println("ModeBの速度の値は " + Setting.ModeB.SPEED); } }
実行結果の例は以下の通り。
ModeAの速度の値は 100 ModeAの位置の値は 50 ModeBの速度の値は 150
内部クラスを使用したことで、SPEEDという名称をモードAとモードBにそれぞれ使用することができました。
内部クラスの修飾子がpublic static
となっている点にも注目してください。
privateなフィールドやメソッドは、外部のクラスからはアクセスできません。ところが、内部クラスからはアクセスすることができます。このため、あるクラスと密接に関わるクラスを作成する場合に、内部クラスとすると実装がシンプルになる場合があります。
S203プロジェクトでは swingを使用してボタンクリックに反応するプログラムを作成しました。S203プロジェクトでは、リスナを MyJFrame自身が実装していましたが、ここでは内部クラスとして分離した例を挙げてみます。
なお、この場合、内部クラスの修飾子はprivateになることが多いです。
ソースコードは以下の通り。
Y202/MyJFrame.java
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; /** * JFrameを継承したクラスです。 */ public class MyJFrame extends JFrame { /** * デフォルトのシリアルバージョン ID(warning回避)。 */ private static final long serialVersionUID = 1L; /** * ボタン1。 */ private JButton jButton1 = new JButton("朝"); /** * ボタン2。 */ private JButton jButton2 = new JButton("夜"); /** * ラベル1。 */ private JLabel jLabel1 = new JLabel("メッセージ欄"); /** * コンストラクタ。 */ public MyJFrame() { // レイアウトを設定する this.getContentPane().setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS)); // フレームにボタンとラベルを登録する this.getContentPane().add(this.jButton1); this.getContentPane().add(this.jButton2); this.getContentPane().add(this.jLabel1); // リスナを設定する this.jButton1.addActionListener(new ActionListener1("おはようございます")); this.jButton2.addActionListener(new ActionListener1("こんばんは")); } /** * アクションリスナインターフェイスを実装した内部クラスです。 */ private class ActionListener1 implements ActionListener { /** * メッセージ。 */ private String message; /** * コンストラクタ。 * @param message メッセージ */ public ActionListener1(String message) { this.message = message; } @Override public void actionPerformed(ActionEvent e) { MyJFrame.this.jLabel1.setText(this.message); } } }
Y202/Y202.java
import javax.swing.JFrame; /** * 内部クラスを利用します。 */ public class Y202 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { JFrame jFrame = new MyJFrame(); jFrame.setTitle("swing test"); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setSize(400, 300); // ウインドウを表示する(新しいスレッドが生成される) jFrame.setVisible(true); } }
実行結果は以下の通り。
MyJFrameクラスの 67行目でMyJFrame.this.jLabel1.~
と記述することで、privateである jLabel1にアクセスできていることが分かります。なお、単にjLabel1.~
と省略して記述しても大丈夫ですが、本ウェブサイトでは一貫して省略をせずに記述することとします。
もし ActionListenerClassクラスを MyJFrameクラスの内部クラスとして宣言しなかった場合には、jLabel1へアクセスするために参照の受け渡しなどの実装を記述する必要があります。内部クラスとしたことで、これらの手間を省くことができました。
インスタンスフィールドとしてではなく、メソッドやコンストラクタの中で、内部クラスを宣言することもできます。以下のプログラムでは、コンストラクタで内部クラスを宣言しています。
基本的には、staticではない内部クラスと同じ利点があります。
ソースコードは以下の通り。
Y203/MyJFrame.java
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; /** * JFrameを継承したクラスです。 */ public class MyJFrame extends JFrame { /** * デフォルトのシリアルバージョン ID(warning回避)。 */ private static final long serialVersionUID = 1L; /** * ボタン1。 */ private JButton jButton1 = new JButton("朝"); /** * ボタン2。 */ private JButton jButton2 = new JButton("夜"); /** * ラベル1。 */ private JLabel jLabel1 = new JLabel("メッセージ欄"); /** * コンストラクタ。 */ public MyJFrame() { // レイアウトを設定する this.getContentPane().setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS)); // フレームにボタンとラベルを登録する this.getContentPane().add(this.jButton1); this.getContentPane().add(this.jButton2); this.getContentPane().add(this.jLabel1); /** * アクションリスナインターフェイスを実装した内部クラスです。 */ class ActionListener1 implements ActionListener { /** * メッセージ。 */ private String message; /** * コンストラクタ。 * @param message メッセージ */ public ActionListener1(String message) { this.message = message; } @Override public void actionPerformed(ActionEvent e) { MyJFrame.this.jLabel1.setText(this.message); } } // リスナを設定する this.jButton1.addActionListener(new ActionListener1("おはようございます")); this.jButton2.addActionListener(new ActionListener1("こんばんは")); } }
Y203/Y203.java
import javax.swing.JFrame; /** * 内部クラスを利用します。 */ public class Y203 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { JFrame jFrame = new MyJFrame(); jFrame.setTitle("swing test"); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setSize(400, 300); // ウインドウを表示する(新しいスレッドが生成される) jFrame.setVisible(true); } }
実行結果は以下の通り。