Javaでは、あるクラスの内部に別のクラスを定義することが可能です。これを内部クラス(Inner Class)と呼びます。主に、特定のクラス内でのみ使用される補助的なクラスをカプセル化し、コードの論理的なグループ化を強化するために利用されます。
1. メンバ内部クラス(Member Inner Class)
外部クラスのメンバ変数と同じレベルで定義されるクラスです。外部クラスのインスタンスに紐付いて動作するため、外部クラスのプライベート変数にも直接アクセスできます。
2. static内部クラス(Static Nested Class)
staticキーワードを付与して定義されるクラスです。外部クラスのインスタンスがなくても生成可能ですが、外部クラスのインスタンス変数(非staticメンバ)には直接アクセスできず、staticメンバのみにアクセス可能です。
3. ローカル内部クラス(Local Inner Class)
メソッドの内部などで定義されるクラスです。そのメソッド内でのみ有効であり、スコープが非常に限定的です。メソッド内のローカル変数にアクセスする場合、その変数は実質的にfinal(再代入不可)である必要があります。
4. 匿名内部クラス(Anonymous Inner Class)
名前を持たないクラスです。インターフェースの即時実装や抽象クラスの継承において、一度限りの利用を目的として使用されます。
// インターフェースの定義
public interface Worker {
void performTask();
}
// 外部クラスの定義
public class Company {
private String companyName;
public static String branchLocation = "Tokyo";
public Company(String companyName) {
this.companyName = companyName;
}
// 1. メンバ内部クラス
public class Department {
private String deptName;
public Department(String deptName) {
this.deptName = deptName;
}
public void printInfo() {
// 外部クラスのプライベートメンバにアクセス可能
System.out.println("Company: " + Company.this.companyName);
System.out.println("Dept: " + this.deptName);
}
}
// 2. static内部クラス
public static class Facility {
private String facilityId;
public Facility(String id) {
this.facilityId = id;
}
public void showLocation() {
// 外部クラスのstaticメンバのみアクセス可能
System.out.println("Location: " + branchLocation);
System.out.println("Facility ID: " + facilityId);
}
}
public void executeOperation() {
// 3. ローカル内部クラス
class InternalProcessor {
void process() {
System.out.println("Processing data within method...");
}
}
InternalProcessor proc = new InternalProcessor();
proc.process();
}
}
各内部クラスの利用例
内部クラスを利用する際は、その種類によってインスタンス化の手順が異なります。特にメンバ内部クラスは、外部クラスのインスタンスを介して生成する必要があります。
public class Main {
public static void main(String[] args) {
// 外部クラスのインスタンス化
Company myCompany = new Company("TechCorp");
// メンバ内部クラスのインスタンス化(外部インスタンス経由)
Company.Department devDept = myCompany.new Department("Engineering");
devDept.printInfo();
// static内部クラスのインスタンス化(直接生成可能)
Company.Facility mainOffice = new Company.Facility("HQ-01");
mainOffice.showLocation();
// メソッド実行(内部でローカル内部クラスを使用)
myCompany.executeOperation();
// 4. 匿名内部クラスの実装
Worker tempStaff = new Worker() {
@Override
public void performTask() {
System.out.println("Anonymous class performing temporary task.");
}
};
tempStaff.performTask();
}
}
主な特徴と注意点
- アクセス制御: 内部クラスは外部クラスのすべてのメンバ(privateを含む)にアクセスできます。
- メモリ管理: 非staticな内部クラスは外部クラスへの参照を保持するため、メモリリークの原因にならないよう注意が必要です。
- 設計指針: 外部から独立して使用されるべきであればstatic内部クラスを、外部クラスの状態に依存するのであればメンバ内部クラスを選択するのが一般的です。