내부(중첩) 클래스(Inner Class, Nested Class) 란?
- 다른 클래스 내부에 선언된 클래스를 의미한다.
- 코드의 가독성이 향상되고 코드의 구조를 파악하기에 용이하다.
- 클래스 간의 관계를 명확하게 표현하여 내부 클래스는 외부 클래스의 멤버에 접근 가능하다.
종류
1. (비정적)내부 클래스(Non - Static Inner Class)
- 반드시 외부 클래스의 인스턴스가 있어야만 생성할 수 있는 클래스이다.
- 외부 클래스의 인스턴스에 속하기에 외부 클래스의 인스턴스 변수 및 메서드에 직접적인 접근이 가능하다.
- 외부 클래스와 밀접한 관련이 있는 경우 사용한다.
<비정적 내부 클래스> - 인스턴스 멤버처럼 사용
class InnerClass{
void display(){
System.out.println("외부 클래스 필드에 접근 : "+outerField);
}
}
<사용방법>
1) 외부 클래스 인스턴스화
OuterClass outerClass1 = new OuterClass();
2) 외부 클래스 내부 클래스 형식으로 내부 클래스 인스턴스를 생성한다.
외부클래스타입.내부클래스타입 변수명 = 외부 인스턴스.new 내부클래스();
2. 정적 내부 클래스(Static Inner Class)
- 외부 클래스의 정적 멤버에 속한다.
- 외부 클래스의 인스턴스 변수에 접근하지 못한다
- 외부 클래스의 정적 멤버에만 접근이 가능하다
- 외부 클래스의 인스턴스 생성 없이 인스턴스화가 가능하다.
static class StaticClass{
void display(){
//System.out.println("외부 클래스 필드에 접근 : "+outerField);-오류발생
//외부 필드에 접근하지 못한다.
System.out.println("외부 클래스 정적 필드에 접근"+staticField);
}
}
외부 클래스타입. 내부 클래스타입 변수명 = new 외부클래스명.내부클래스();
OuterClass.StaticClass staticClass = new OuterClass.StaticClass();
staticClass.display();
3. 메서드 내부 클래스(지역 클래스)(Method Inner Class)
- 메서드 내에서 정의된 내부 클래스이다
- 해당 메서드 내에서만 사용이 가능하다
- 메서드의 지역변수에만 접근이 가능하다.
- 로컬 변수(외부 클래스 멤버 변수)가 final인 경우에만 접근이 가능하다.
>>>> 외부에 있는 로컬 변수 아무렇게나 접근해서 변수값을 바꿔버릴가봐 final 인 경우에만 접근이 가도록 설정할 할 수 있다.
외부에서 인스턴스화가 불가능 하기 때문에 다음과 같이 인스턴스화도 내부에서 해 주어야 한다.
void outerMethod() {
String localVar = "메서드 로컬 변수";
// localVar = "메서드 로컬 변수 값 변경"; - 지역 변수는 암묵적으로 final로 간주 (생략 가능)
final String localFinalVar = "메서드 로컬 변수 (Final)";
// localFinalVar = "메서드 로컬 변수 값 변경";
class MethodClass {
void display() {
System.out.println("로컬 변수에 접근(Final X): " + localVar);
System.out.println("로컬 변수에 접근(Final O): " + localFinalVar);
}
}
System.out.println("외부 클래스의 메서드를 호출");
// 로컬 클래스의 인스턴스화
// : 메서드 내부에서만 쓰이기 때문에 인스턴스화도 내부에서 일어남
MethodClass methodClass = new MethodClass();
methodClass.display();
}
System.out.println("=== 메서드 내부 클래스 (지역 클래스) ===");
OuterClass outerClass2 = new OuterClass();
outerClass2.outerMethod();
4. 익명(내부)클래스(Anonymous Inner Class)
이름이 없는 내부 클래스, 주로 인터페이스나 추상 클래스의 구현에 사용한다.
즉시 객체 생성이 가능하고 주로 클래스를 일회성으로 사용하는 경우에 사용한다.
예제 1)
abstract class Greeting {
abstract void sayHello();
}
public class Main {
public static void main(String[] args) {
// 첫 번째 익명 내부 클래스 인스턴스
Greeting greeting1 = new Greeting() {
private int count = 0; // 익명 내부 클래스의 멤버 변수
@Override
void sayHello() {
count++;
System.out.println("Hello from greeting1! Count: " + count);
}
};
// 두 번째 익명 내부 클래스 인스턴스 (greeting1과 완전히 별개)
Greeting greeting2 = new Greeting() {
private int count = 0; // 새로운 멤버 변수 (greeting1과 공유되지 않음)
@Override
void sayHello() {
count++;
System.out.println("Hello from greeting2! Count: " + count);
}
};
// 각각 호출해보기
greeting1.sayHello(); // Hello from greeting1! Count: 1
greeting1.sayHello(); // Hello from greeting1! Count: 2
greeting2.sayHello(); // Hello from greeting2! Count: 1
greeting2.sayHello(); // Hello from greeting2! Count: 2
}
}
예제 2)
// 추상 클래스: 하나 이상의 추상 메서드를 포함
abstract class AbstractClass {
// cf) 추상 클래스는 구현 클래스를 가질 수 있음
// >> 추상 메서드에 abstract 키워드 생략 X
abstract void display(); // 추상 메서드: 구현부 {}가 없는 메서드
}
// 인터페이스
interface InterfaceClass {
// cf) 필드는 public static final 생략
// cf) 인터페이스는 일반 구현 클래스를 가질 수 없음
// >> static 메서드, default 메서드 가능 (예외)
void something(); // public abstract 생략
}
<Main>
System.out.println("=== 익명 (내부) 클래스 ===");
// 클래스타입 변수명 = new 클래스명() { 메서드 구현 - 추상클래스/인터페이스의 추상 메서드 }
AbstractClass abstractClass1 = new AbstractClass() {
@Override
void display() {
System.out.println("익명 내부 클래스 - 추상 클래스1");
}
};
abstractClass1.display();
AbstractClass abstractClass2 = new AbstractClass() {
@Override
void display() {
System.out.println("익명 내부 클래스 - 추상 클래스2");
}
};
abstractClass2.display();
System.out.println(abstractClass1 == abstractClass2); // false
// >> 재사용될 필요가 없거나, 단 한번의 사용 목적에 유용
InterfaceClass interfaceClass = new InterfaceClass() {
@Override
public void something() {
System.out.println("안녕하세요! 익명 클래스입니다 :)");
}
};
interfaceClass.something();
}
Java의 내부 클래스(Inner Class)와 익명 클래스(Anonymous Class)는 코드의 가독성과 유지보수성을 높이면서, 외부 클래스와 밀접하게 연결된 기능을 모듈화할 수 있는 강력한 도구입니다. 특히, 익명 클래스는 단 한 번 사용될 간단한 로직을 구현할 때 매우 유용합니다. 추상 클래스나 인터페이스를 손쉽게 구현할 수 있으므로, 필요한 순간에 간단히 사용할 수 있습니다.
내부 클래스 비교 정리
| 비정적 내부 클래스 | 외부 클래스의 인스턴스가 필요 | outer.new Inner(); | O (가능) |
| 정적 내부 클래스 | 외부 클래스의 인스턴스 없이 사용 가능 | new Outer.StaticInner(); | X (불가능) |
| 메서드 내부 클래스 | 메서드 내부에서만 사용 가능 | 메서드 내부에서 직접 생성 | O (가능, 단 final 지역 변수만 접근 가능) |
| 익명 내부 클래스 | 일회성 사용, 인터페이스/추상 클래스 구현 | 클래스 선언 없이 즉시 객체 생성 | O (가능) |
내부 클래스를 선택할 때는 각 클래스 유형의 특성과 목적을 잘 파악하여, 비정적/정적/지역/익명 클래스 중 알맞은 방식을 적용하면 됩니다. 이로써 코드 구조를 명확히 하고, 로직을 한눈에 파악할 수 있게 만들 수 있습니다.
'웹개발 > JAVA' 카테고리의 다른 글
| 캡슐화(Encapsulation// getter&setter) (1) | 2025.03.08 |
|---|---|
| 람다식(Lambda)– 기본 문법 (1) | 2025.03.07 |
| 결합도(Coupling)와 응집도(Cohesion) (0) | 2025.03.05 |
| SOLID 원칙 (0) | 2025.03.04 |
| API - Java Util Package (1) | 2025.03.03 |