検索

キーワード


【Java】ネストクラスとは?4つのクラスの違いや書き方を解説

  • 公開日:2020-09-17 14:27:04
  • 最終更新日:2021-01-25 18:50:49
【Java】ネストクラスとは?4つのクラスの違いや書き方を解説

こんにちは、駆け出しプログラマーの若江です!

ここでは初学者として学習を終えた私からアウトプットの意味も込めて、

ネストクラスについて紹介させていただきます。

できる限り初学者が理解しやすい内容として紹介させていただくので、参考となれば幸いです!


関連記事リンク:static の紹介 / 抽象クラスの紹介 / インターフェースの紹介 / 変数と定数の紹介 / イニシャライザの紹介 / アクセス修飾子の紹介 / final 修飾子の紹介 / メソッドの紹介 / インスタンスの紹介 / thisの紹介

ネストクラスの種類と使い方

ネストとは「入れ子」を意味し、入れ子構造となったクラスをネストクラスと呼びます。

入れ子構造になっている必要があるためネストクラス単体を生成することができず、

主に3つの場所で生成することができるようになっています。

「クラス直下」「メソッド直下」「インスタンス生成時」

これら3つの場所で生成されるネストクラスの特徴を見ながら、

ネストクラスについて理解を深めていきましょう。

ここではネストクラスの役割や使い方について紹介させていただきます。


ネストクラスの役割

ネストクラスを使うのは主に以下のような場合です。


クラスの利用が限定的

その場限りで利用したいクラスがあった場合に、

新たにクラス生成をするのではなく、ネストクラスで対応することができます。


入れ子の外側と内側の関係性がわかりやすい

「その場限りで利用」するクラスがネストクラスであるため、

主に入れ子の外側から内側へアクセスするか、入れ子の内側から外側へアクセスする目的で記述されます。

そのため入れ子構造となった外側と内側でお互いがどのような関係にあるかわかりやすくなります。


ネストクラスを隠蔽できる

ネストクラスはクラスの中やメソッドの中などで記述されるため、

言わば何か(クラスやメソッド)に包まれた状態のクラスと言えます。

包みの外側から見るとネストクラスは見えないため隠蔽することができます。


誤参照の防止

ネストクラスは入れ子関係以外の外部から参照することは可能です。

しかしネストクラスがある場所を明示的に指定しなければアクセスできませんし、

ネストクラスは protected や private、デフォルトなどのアクセス修飾子を付けることができるため、

そもそも入れ子関係にないものはアクセスできないようにすることもできます。

このような特徴から「誤って参照先に指定してしまった」というミスを事前に防ぐことができます。


ネストクラスの種類

ネストクラスは3つの場所と、1つの static 領域で記述することができます。

それぞれ「インナークラス(非static 内部クラス)」「ローカルクラス」「匿名クラス」「static クラス

と呼ばれるネストクラスです。

これらは「static クラス」も含めて全てインスタンス化しないと、その機能を利用することはできません。

以降ではネストクラスの記述や呼び出し方、インスタンス化の方法やそれぞれの特徴について見ていきましょう。



インナークラス(非static 内部クラス)とは?

クラスの直下で作ることができるクラスです。

入れ子関係となったクラスをそれぞれ、

入れ子の外側を「外部クラス(アウタークラス)」、

入れ子の内側を「内部クラス(インナークラス)」と呼びます。

インナークラスは外部クラスのメンバーとなり、

外部クラスのフィールドやメソッドと同じように扱われるため、別名「メンバークラス」とも呼ばれます。


インナークラスの書き方

インナークラスは通常のクラス宣言と同様、class を宣言記述します。

ただし public 以外のアクセス修飾子を付けることができる点で少し異なります。

・インナークラス基本構文: アクセス修飾子 class クラス名{ 内容 }


実際には以下のような書き方になります。


◆インナークラス記述例

public class OuterClass {

	// インナークラス	
	public class InnerClass{
	}
}


インナークラスの使い方

インナークラスはアウタークラスのメンバーであるため、

インナークラスからアクセス修飾子が private なアウタークラスのメンバーにもアクセスすることができます。


◆インナークラスと private のアクセス例

public class OuterClass {
	private int num = 1;
	private void outerId() {
	}

	// インナークラス
	public class InnerClass{
		// アウタークラスの private メソッドとフィールドへアクセス
		public void innerId() {
			outerId();
			System.out.println(num);
		}
	}
}


インナークラスからアウタークラスの private なメソッドとフィールドへアクセスできました。

基本的にクラス間でアクセスを行うためにはインスタンスを生成しなくてはなりませんが、

インナークラスの存在はアウタークラスの存在が前提条件のため、

インスタンス生成をしなくても直接アクセスが可能となっています。

逆にアウタークラスからインナークラスへアクセスする場合は、インスタンス生成が必須です。


同名のメンバーを持つ事が可能

アウタークラスとインナークラスのインスタンスはそれぞれで生成されます。

そのためアウタークラスとインナークラスで同名のメソッドやフィールドを持つことも可能です。

public class OuterClass {
	private int num = 1;
	private void Id() {
	}

	// インナークラス
	public class InnerClass{
		// アウタークラスと同名のメソッド
		public void Id() {
		}
	}
}


アウタークラスとインナークラスで同名のメソッドやフィールドを持った場合、

this を使ってインナークラスからアウタークラスを呼び出すこともできます。

・アウタークラスのメンバー呼び出し(this): アウタークラス名.this.メンバー名;


◆アウタークラスのフィールド呼び出し例

public class OuterClass {
	private int num = 1;

	// インナークラス
	public class InnerClass{
		public void Id(int num) {
			OuterClass.this.num = num; // this を使ってアウタークラスのフィールドへ代入
		}
	}
}


ちなみにアウタークラス名を省いた「this.メンバー名」は、インナークラス自身を指します。

例えば上記「OuteClass.this.num」を「this.num」にすると、InnerClass の num を探します。

※今回はインナークラスのフィールドに num がないためエラーとなります。


余談

実用的ではありませんが、

インナークラスの中に更にインナークラスを持たせることも可能です。


インナークラスのインスタンス生成方法

入れ子関係にあるアウタークラスでインナークラスのインスタンスを生成する場合は、

アウタークラス名、インナークラス名の順で指定することでインスタンス生成ができます。

・アウタークラスでインナークラスのインスタンス生成 基本構文:

 アウタークラス名.インナークラス名 変数名 = new アウタークラス名.インナークラスコンストラクタ();


また上記のアウタークラス名を省略して、通常のインスタンス生成と同じような記述も可能です。

・アウタークラス名を省略してインナークラスをインスタンス生成:

 インナークラス名 変数名 = new インナークラスコンストラクタ();


◆アウタークラスでインナークラスのインスタンス生成例

public class OuterClass {

	// インナークラス
	public class InnerClass{
	}

	// アウタークラス名.インナークラス名でインスタンス生成
	OuterClass.InnerClass test = new OuterClass.InnerClass(); 
	// アウタークラス名省略でインスタンス生成
	InnerClass test2 = new InnerClass();
}


入れ子関係にないクラスでインナークラスのインスタンスを生成する場合は、

アウタークラスのインスタンスを生成した後に、

インナークラスのインスタンスを生成するという手順を踏まなくてはなりません。

・インナークラスのインスタンス生成基本構文: 

 アウタークラス名 変数名① = new アウタークラスコンストラクタ名();

 アウタークラス名.インナークラス名 変数名② = 変数名①.new インナークラスコンストラクタ名(); 


上記を省略して1行で記述することも可能です。

・インナークラスのインスタンス生成(省略):

 アウタークラス名.インナークラス名 変数名 = new アウタークラスコンストラクタ名().new インナークラスコンストラクタ名();


実際のインスタンス生成方法2つをサンプルコードで見てみましょう。

◆インナークラスのインスタンス生成方法

【クラス①】 ネストクラスを持つクラス

public class OuterClass {

	// インナークラス
	public class InnerClass{
	}
}

【クラス②】 インナークラスをインスタンス化するクラス

public class Test{
	public static void main(String[] args) {

		// インナークラスのインスタンス生成基本構文
		OuterClass test = new OuterClass();
		OuterClass.InnerClass test2 = test.new InnerClass();

		// インナークラスのインスタンス生成省略
		OuterClass.InnerClass test3 = new OuterClass().new InnerClass();

	}
}



ローカルクラスとは?

ローカルクラスは、メソッド内で作成できるクラスです。

ローカル変数と同じように、ローカルクラスの有効範囲はそのメソッド内に限られます。

メソッド内で使うことを目的としているため、アクセス修飾子や static 修飾子の使用はできません。


ローカルクラスの書き方

ローカルクラスはメソッド内で通常のクラス同様に class 宣言をして作成します。

ただしアクセス修飾子が付けられない点で少し異なります。

・ローカルクラスの基本構文: class クラス名 { 内容 }


◆ローカルクラスの例

public class NestClassTest {

	// メソッド
	void method() {
		
		// ローカルクラス
		class LocalClass{
		}
	}
}


ローカルクラスの使い方

ローカル変数へのアクセス

ローカルクラスは、同メソッド内のローカル変数へアクセスすることも可能です。

このときローカルクラスからローカル変数へアクセスできるのは、final 修飾子が付いたローカル変数のみです。


◆ローカルクラスからローカル変数へアクセスする例

public class NestClassTest {

	// ローカル変数 a と num を持つメソッド
	void method(final int a) {
		final int num = 1;
		
		// ローカルクラス
		class LocalClass{
			int number = num; // ローカル変数 num を参照
			int b = a; // ローカル変数 a を参照
		}
	}
}


ローカル変数の final 修飾子は JavaSE8 から暗黙的に付与されるようになったため、

記述を省略することができるようになりました。

ただしローカル変数へ再代入などを行うと、

暗黙的な final の付与はされなくなり変数として扱われるため、

コンパイルエラーを起こしますので注意が必要です。


◆上記サンプルコードからfinal 修飾子省略

public class NestClassTest {

	void method(int a) {
		int num = 1;
		
		class LocalClass{
			int number = num;
			int b = a;
		}
	}
}

再代入を行うと変数扱いとなり、ローカルクラスの num や a がコンパイル時にエラーとなります。

public class NestClassTest {

	void method(int a) {
		int num = 1;
		
		// num と a に再代入
		num = 2;
		a = 3;
		
		class LocalClass{
			int number = num; // num に final 修飾子が付いていないためエラー
			int b = a; // a に final 修飾子が付いていないためエラー
		}
	}
}


※メソッド側からローカルクラスへアクセスするには、

 インナークラスと同じように、ローカルクラスのインスタンスを生成してアクセスします。


アウタークラスへのアクセス

アウタークラス(メソッドが書かれているクラス)のインスタンス変数へのアクセスは、

通常のアクセス方法でアクセス可能です。

public class NestClassTest {
	int num = 0; // インスタンス変数
	
	void method() {
		class LocalClass{
			int number = num; // ローカルクラスの変数にインスタンス変数を代入
		}
	}
}


abstract 修飾子と final 修飾子

ローカルクラスには abstract 修飾子と final 修飾子をつけることが可能です。

ローカルクラスの abstract 修飾子は継承を前提に作られ、final 修飾子は継承を不可にするため、

通常のクラスと同様に abstract と final を宣言することができます。


◆ abstract 修飾子の使用例

public class NestClassTest {

	void method() {
		
		// ローカルクラスの抽象クラス
		abstract class LocalClass{
			abstract void sample(); // 抽象メソッド
		}

		// 上記 LocalClass を継承して sample メソッドをオーバーライド
		class LocalClass2 extends LocalClass{
			void sample() {
			}
		}
	}
}

◆ final 修飾子の使用例

public class NestClassTest {

	void method(int a) {
		
		final class LocalClass{
		}

		// final な LocalClass を継承しようとしたためエラー
		class LocalClass2 extends LocalClass{
		}
	}
}


余談

ローカルクラスのインスタン生成は通常のインスタンス生成と同じ方法です。

ただしメソッド内でローカルクラスをインスタンス化する際、

ローカルクラス記述以降でインスタンス生成しないとエラーになるため注意が必要です。

またアウタークラス(メソッドが書かれているクラス)ではローカルクラスのインスタンスは生成できません。



匿名クラス(無名クラス)とは?

匿名クラスは、インスタンス生成時に作成できるクラスです。

1つのインスタンスでしか使用しないネストクラスが必要な際に使います。

匿名(無名)の通り、名前を持たないクラスです。

クラス名がないため、コンストラクタを持っていません。


匿名クラスの書き方

匿名クラスはインスタンス生成時のコンストラクタ以降に「 { } 」を続けて記述します。

・匿名クラスの基本構文: クラス名 変数名 = new コンストラクタ名() { 匿名クラスの処理内容 };


◆匿名クラスの記述例

public class Test{
	public static void main(String[] args) {
		NestClassTest test = new NestClassTest() {
		};
	}
}


クラスの宣言や命名がないため少しわかりにくいですが、

インスタンス生成時の末尾に「 { } 」が付いていると、そのカッコ内が匿名クラスです。


匿名クラスの使い方

インターフェースや抽象クラスの実装

インターフェースや抽象クラスをインスタンス生成と同じタイミングで実装/継承することができます。


◆インターフェースを匿名クラスで実装した例

【インターフェース】 定数 num と抽象メソッド sample を作成

public interface NestClassTest2 {
	int num = 0;

	void sample();
}

【クラス】 インターフェースの型を使って匿名クラスで実装、インスタンス化

public class Test{
	public static void main(String[] args) {

		NestClassTest2 test = new NestClassTest2(){
			public void sample() {
				System.out.println("匿名クラス");
			}
		};

		test.sample(); // 結果:「匿名クラス」 を表示
		System.out.println(test.num); // 0 を表示
	}
}


インターフェースのメソッドを匿名クラスで実装しています。

このように匿名クラスを使うと実装や継承の宣言なしで実装/継承ができます。

変数 test でインターフェースの定数 0 と、実装メソッドの出力「匿名クラス」を呼び出せました。


イニシャライザ

匿名クラスはコンストラクタを持つ事はできませんが、イニシャライザを持つことはできます。

※イニシャライザの知識が無い方は別記事で紹介させていただいているのでご一読ください。


◆匿名クラスでイニシャライザを使う例

【クラス①】 add メソッドを呼ぶと、フィールドの値を 1 追加する。getNum メソッドで num の値を取得

public class NestClassTest {
	private int num = 0;

	public void add() {
		++num;
	}

	public int getNum() {
		return num;
	}
}

【クラス②】 匿名クラスでイニシャライザを使って初期値を設定

public class Test{
	public static void main(String[] args) {

		NestClassTest test = new NestClassTest(){
			{
				add(); // num に 1 追加
			}
		};

		System.out.println(test.getNum()); // 結果:1 を表示
	}
}


インスタンス生成の右記から始まる最初の「 { 」は匿名クラスの先頭です。

次行の「 { 」はイニシャライザの先頭です。add メソッドを呼び「 } 」でイニシャライザの範囲を括ります。

最後に「 }; 」で匿名クラスの範囲を括ります。

値取得メソッド getNum で値 1 を取得できました。


余談

ここでは扱いませんが、匿名クラスは Java8 で新たに機能実装された、

関数型インターフェースやラムダ式といった比較的新しい機能との関りを持つネストクラスです。



static クラスとは?

先ほど紹介したインナークラスに static を付けると static クラス扱いとなります。

クラス作成できる場所は同じですが、stataic クラスがアクセスできるのは static なものに限るため、

インナークラスとは全く異なる働きをします。

そのことでアウタークラスとの関係性が薄まるため、ほとんど別もののクラスと同じ役割をします。


static クラスの書き方

static クラスはインナークラスの書き方に static 修飾子を追加します。

・sitatic クラスの基本構文: アクセス修飾子 static class クラス名 { 内容 }


◆ static クラスの作成例

public class OuterClass {

	// staticクラス
	public static class StaticClass{
	}
}


static クラスの使い方

外部の static なものへアクセス

アクセス修飾子の有効範囲に寄りますが、

static なものであれば他クラスやパッケージ外のクラスのものにもアクセスすることができます。


◆ static クラスのアクセス例

【他クラス】

public class NestClassTest {
	public static int number = 10;
}

【入れ子関係にあるクラス】

public class OuterClass {

	static int num = 1;

	// staticクラス:アウタークラスの static フィールド、他クラスの static フィールドへアクセス
	public static class StaticClass{
		static int id = OuterClass.num + NestClassTest.number; // num の 1 と number の 10 を足す
	}
}


アウタークラスの static フィールドと、他クラスにある static フィールドを呼び出しています。

通常のクラスを作成したような場合と同じように static なものへアクセスできます。


アウタークラスの非static からアクセス

アウタークラスの非static なものから、static クラスへのアクセスは可能です。

アウタークラスから static クラスへアクセスする場合は、

static クラス名.static クラスのメンバー名でアクセスができます。


◆アウタークラスの非static なものから static クラスへのアクセス例

public class OuterClass {

	int i = StaticClass.num; // static クラスの static フィールドへアクセス

	public static class StaticClass{
		static int num = 1;
	}
}


非static なメソッドやフィールドの記述

static クラスの中に非static なメソッドやフィールドを作成することは可能です。

これらは主に static クラス内の処理で使うために存在します。


◆ static クラスで非static フィールドを作成する例

【他クラス】

public class NestClassTest {
	public static int number = 10;
}

【static クラス】

public class OuterClass {

	static int num = 1;

	public static class StaticClass{
		static int id = OuterClass.num + NestClassTest.number;
		int total = id * 10; // static フィールド id に 10 を積算して非static フィールド total で保持
	}
}


static クラスの呼び出し方

static なものはインスタンス生成できないですが、

冒頭でも紹介した通り static クラスにおいてはインスタンス生成が必要です。

ここでの static はアウタークラスのインスタンスを生成することなく、

static クラスのインスタンス生成ができるという特徴を持っています。

・他クラスでの static クラスのインスタンス生成基本構文:

 アウタークラス名.static クラス名 変数名 = new アウタークラス名.static クラス名();


先ほどの紹介例で使った OuterClass の StaticClass をインスタンス化してみましょう。


◆ static クラスのインスタンス生成例

public class Test{
	public static void main(String[] args) {

		OuterClass.StaticClass test = new OuterClass.StaticClass();

	}
}


アウタークラスのインスタンスは生成せずに明示的に static クラスを指定して、

static クラスをインスタンス化することができました。



まとめ

ネストクラスは入れ子にする場所によって違った働きをするため、

覚えることは多めですが、開発の中ではネストクラスが必要になるシーンもあります。

ネストクラスのコーディングに出会っても落ち着いてコードを読めるように知識を付けておきましょう。


関連記事リンク

static の紹介 / 抽象クラスの紹介 / インターフェースの紹介 / 変数と定数の紹介 / イニシャライザの紹介 / アクセス修飾子の紹介 / final 修飾子の紹介 / メソッドの紹介 / インスタンスの紹介 / thisの紹介


【著者】

若江

30代で異業種となるIT業界へ転職した駆け出しのプログラマです。これまで主に Java や Ruby、HTML/CSS を使って学習を目的としたショップサイトや掲示板サイトの作成を行いました。プログラマとしての経験が浅いからこそ、未経験者の目線に近い形で基礎の紹介をしていきたいと思います。

よく読まれている記事
【Java】JSPでタグライブラリを使う(JSTL)

【Java】JSPでタグライブラリを使う(JSTL)

こんにちは。エンジニアの新田です!ここでは、システムエンジニアとして働いている私が、システム開発手法や開発言語について紹介していこうと思います。今回は、JSPの標準タグライブラリ「JSTL」について紹介します。Javaについて勉強している方、Webアプリケーションを構築したいと思っている方の参考になれば幸いです!関連記事リンク: 【Java】JSPの基本的な構文/【Java】JSPのアクションタグ

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!そもそもStringとは何?『 String 』... Java言語において文字列のデータ型を指します。基本デ

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!Stringクラスformatメソッドの文字列整形【java.utilパッケージ】Formatterクラスfo

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者にも✅、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!文字列を扱う3つのクラス【java.langパッケージ】java.langパッケージの文字列を扱うクラスにはS

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!プリミティブ型と参照型プログラム開発では型を持った変数を使ってデータのやり取りをしますが、データ型によって仕様