Javaにおけるネストされたクラスとは
Javaでは、あるクラスの内部に別のクラスを定義することができます。このようなクラスを「ネストされたクラス(Nested Class)」と呼びます。ネストされたクラスは、コードの構造化、カプセル化の強化、可読性の向上など、様々な目的で利用されます。ネストされたクラスには、外部クラスのメンバーへのアクセス権限や、それ自体のインスタンス化のされ方によっていくつかの種類があります。
class OuterContainer { // 外部クラス
private String containerName; // 外部クラスの属性
public OuterContainer(String name) {
this.containerName = name;
}
public void displayOuterName() {
System.out.println("コンテナ名: " + containerName);
}
// 内部に定義されるネストされたクラス
class InnerComponent {
public void showParentName() {
// 内部クラスは外部クラスのprivateメンバーに直接アクセス可能
System.out.println("親コンテナ名 (内部より): " + OuterContainer.this.containerName);
}
}
}
ネストされたクラスの分類
ネストされたクラスは、大きく以下の2つのカテゴリに分けられ、さらに細分化されます。
- 静的ネストクラス (Static Nested Class):
static修飾子を持つクラスです。外部クラスのインスタンスなしで直接インスタンス化できます。外部クラスの静的メンバーにのみ直接アクセスできます。 - 非静的ネストクラス (Non-static Nested Class) または 内部クラス (Inner Class): 外部クラスのインスタンスと関連付けられています。外部クラスのすべてのメンバー(privateを含む)にアクセスできます。
- メンバー内部クラス (Member Inner Class): 外部クラスのメンバー(フィールドやメソッドと同じレベル)として定義されます。
- ローカル内部クラス (Local Inner Class): メソッドや特定のブロック内で定義されます。定義されたスコープ内でのみアクセス可能です。
- 匿名内部クラス (Anonymous Inner Class): 名前のない一時的なクラスで、通常はインターフェースの実装や抽象クラスの拡張、または特定クラスの継承をその場で一度だけ行うために使用されます。
ローカル内部クラス
ローカル内部クラスは、メソッドやコードブロックの内部で定義されるクラスです。そのスコープは定義されたメソッドまたはブロック内に限定されます。アクセス修飾子(public, private, protected)は使用できませんが、final修飾子を付けることは可能です。外部クラスのすべてのメンバー、および定義されたブロックの「事実上のfinal」なローカル変数にアクセスできます。
class TaskProcessor { // 外部クラス
private int baseValue = 100;
public void executeProcess(int multiplier) { // メソッド
final String processStatus = "準備完了"; // ローカル変数は事実上のfinalである必要があります
// メソッド内で定義されるローカル内部クラス
class OperationExecutor {
private String operationName = "データ処理";
public void runOperation() {
// 外部クラスのメンバーに直接アクセス可能
System.out.println("ベース値: " + baseValue);
// メソッドの引数にもアクセス可能
System.out.println("乗数: " + multiplier);
// ローカル変数にもアクセス可能
System.out.println("現在のステータス: " + processStatus);
System.out.println("操作名: " + operationName);
int result = baseValue * multiplier;
System.out.println("計算結果: " + result);
// 外部クラスのメンバーと名前が重複する場合、外部クラス名.this.メンバー名 でアクセス
// System.out.println("外部クラスのbaseValue: " + TaskProcessor.this.baseValue);
}
}
// ローカル内部クラスは、定義されたメソッド内でのみインスタンス化および使用が可能
OperationExecutor executor = new OperationExecutor();
executor.runOperation();
}
public static void main(String[] args) {
TaskProcessor processor = new TaskProcessor();
processor.executeProcess(5);
}
}
匿名内部クラス
匿名内部クラスは、名前を持たない内部クラスです。通常、インターフェースの実装、抽象クラスの具象化、または既存のクラスの継承を一度きり行う際に使用されます。コードを簡潔にし、使い捨てのオブジェクトを作成するのに適しています。インスタンス化と同時にクラス定義を行うため、その場で動作を記述できます。
インターフェースを実装する匿名内部クラスの例
interface ActionPerformer { // インターフェース
void perform();
}
class SystemUtility {
public void setupAction() {
// ActionPerformerインターフェースを匿名内部クラスで実装
ActionPerformer myAction = new ActionPerformer() {
private String message = "匿名アクションを実行中...";
@Override
public void perform() {
System.out.println(message);
// 外部クラスのメンバーにアクセスしたい場合は SystemUtility.this.XXX
}
};
myAction.perform(); // 匿名内部クラスのメソッドを呼び出す
// 別の匿名内部クラスで直接呼び出す例
new ActionPerformer() {
@Override
public void perform() {
System.out.println("別の短時間のタスクを実行しました。");
}
}.perform();
}
public static void main(String[] args) {
new SystemUtility().setupAction();
}
}
クラスを継承する匿名内部クラスの例
abstract class LoggerBase { // 抽象クラス
private String tag;
public LoggerBase(String tag) { this.tag = tag; }
public abstract void logMessage(String message); // 抽象メソッド
public String getTag() { return tag; }
}
class DataProcessor {
public void processData() {
// LoggerBase抽象クラスを匿名内部クラスで具象化
LoggerBase consoleLogger = new LoggerBase("CONSOLE_LOG") {
@Override
public void logMessage(String message) {
System.out.println("[" + getTag() + "] " + message);
}
};
consoleLogger.logMessage("データ処理開始。");
// 既存の具象クラスを継承する匿名内部クラスの例
class ReportGenerator {
public void generate() {
System.out.println("標準レポート生成。");
}
}
ReportGenerator customReport = new ReportGenerator() {
@Override
public void generate() {
System.out.println("カスタムレポート生成中...");
}
};
customReport.generate();
}
public static void main(String[] args) {
new DataProcessor().processData();
}
}
メソッド引数としての匿名内部クラス
匿名内部クラスは、メソッドの引数として直接渡すことで、簡潔で柔軟なコードを実現できます。特にコールバックやイベントハンドラーの実装でよく利用されます。
interface CallbackHandler {
void handle(String event);
}
class EventManager {
// コールバックハンドラーを引数に取る静的メソッド
public static void registerEventHandler(String eventType, CallbackHandler handler) {
System.out.println("イベントタイプ: " + eventType);
handler.handle("イベント「" + eventType + "」が発生しました。");
}
public static void main(String[] args) {
// 匿名内部クラスをメソッド引数として直接渡す
EventManager.registerEventHandler("CLICK", new CallbackHandler() {
@Override
public void handle(String event) {
System.out.println("クリックイベントを処理: " + event);
}
});
EventManager.registerEventHandler("LOAD", new CallbackHandler() {
@Override
public void handle(String event) {
System.out.println("ロードイベントを処理中: " + event.toUpperCase());
}
});
}
}
メンバー内部クラス
メンバー内部クラスは、外部クラスのインスタンスのメンバーとして定義されます。フィールドやメソッドと同様に、外部クラスのあらゆるメンバー(privateなものを含む)にアクセスできます。メンバー内部クラスのインスタンスを生成するには、まず外部クラスのインスタンスが必要です。
class ApplicationConfig { // 外部クラス
private String appName = "MyApplication";
public int version = 1;
// メンバー内部クラス
// アクセス修飾子(public, private, protected)を適用可能
public class ConfigurationDetail {
private String settingGroup = "General";
public void displaySettings() {
System.out.println("設定グループ: " + settingGroup);
// 外部クラスのメンバーに直接アクセス可能
System.out.println("アプリケーション名: " + appName);
System.out.println("バージョン: " + version);
}
}
// 外部クラスからメンバー内部クラスを使用する例
public void showAppInfo() {
ConfigurationDetail detail = new ConfigurationDetail(); // 外部クラス内では直接インスタンス化可能
detail.displaySettings();
}
// 外部クラスからメンバー内部クラスのインスタンスを返すメソッド
public ConfigurationDetail getDetailInstance() {
return new ConfigurationDetail();
}
public static void main(String[] args) {
ApplicationConfig config = new ApplicationConfig();
// 外部のクラスからメンバー内部クラスのインスタンスを生成する方法 (1)
// 外部クラスのインスタンスを介して 'new InnerClass()' を呼び出す
ApplicationConfig.ConfigurationDetail detail1 = config.new ConfigurationDetail();
detail1.displaySettings();
System.out.println("---");
// 外部クラスのメソッドを介してインスタンスを取得する方法 (2)
ApplicationConfig.ConfigurationDetail detail2 = config.getDetailInstance();
detail2.displaySettings();
System.out.println("---");
// 外部クラスのメソッド内で直接使用
config.showAppInfo();
}
}
静的ネストクラス
静的ネストクラスは、static修飾子を持つネストされたクラスです。外部クラスのインスタンスを必要とせず、直接インスタンス化できます。通常のトップレベルクラスと同様に振る舞いますが、外部クラスの名前空間内に存在します。静的ネストクラスは、外部クラスの静的メンバーにのみ直接アクセスできます(非静的メンバーにアクセスするには、外部クラスのインスタンスが必要です)。
class SystemSettings { // 外部クラス
private static String systemName = "Global System";
private String instanceId = "Default-001"; // 非静的メンバー
// 静的ネストクラス
// アクセス修飾子を適用可能
public static class Preference {
private String currentTheme = "Dark";
public void showPreferences() {
System.out.println("現在のテーマ: " + currentTheme);
// 静的ネストクラスは外部クラスの静的メンバーにのみ直接アクセス可能
System.out.println("システム名 (外部静的): " + systemName);
// System.out.println(instanceId); // エラー: 非静的メンバーには直接アクセスできない
}
// 静的ネストクラス自体も静的メソッドを持つことができる
public static void displaySystemInfo() {
System.out.println("静的メソッドからシステム名: " + systemName);
}
}
// 外部クラスから静的ネストクラスを使用する例
public void displayInstanceSettings() {
Preference pref = new Preference();
pref.showPreferences();
System.out.println("インスタンスID (外部非静的): " + instanceId);
}
public static void main(String[] args) {
// 静的ネストクラスは外部クラスのインスタンスなしで直接インスタンス化可能 (方法1)
SystemSettings.Preference globalPref = new SystemSettings.Preference();
globalPref.showPreferences();
System.out.println("---");
// 静的ネストクラスの静的メソッドも直接呼び出し可能
SystemSettings.Preference.displaySystemInfo();
System.out.println("---");
// 外部クラスのインスタンスを作成し、そこから利用することも可能だが、必須ではない
SystemSettings settings = new SystemSettings();
settings.displayInstanceSettings();
}
}