내부 클래스는 그 이름에서 알 수 있듯이, 하나의 클래스 안에 또 다른 클래스가 정의되어 있는 형태를 말한다. 이 구조는 특정 클래스 내에서만 사용되는 보조 클래스를 그 범위 내에서만 유지하여 외부에는 노출시키지 않는 캡슐화를 강화할 수 있다. 내부 클래스는 주로 외부 클래스와 강하게 연결된 작업을 수행하거나, 외부 클래스의 코드를 간결하게 만들기 위해 사용된다.
이렇게 내부 클래스를 사용함으로써, 개발자는 더욱 체계적이고 구조화된 코드를 작성할 수 있다. 코드의 재사용성과 유지보수성이 향상되며, 복잡한 문제를 좀 더 단순하고 직관적으로 해결할 수 있는 방법을 제공한다. 또한, 이벤트 드리븐 프로그래밍에서는 콜백 함수나 이벤트 핸들러를 내부 클래스를 통해 구현함으로써, 이벤트 처리 로직을 효과적으로 캡슐화할 수 있다.
public class OuterClass {
private int outerField = 10;
// Member Inner Class
public class InnerClass {
public void innerMethod() {
System.out.println("Inner method with outerField value: " + outerField);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.innerMethod(); // Output: Inner method with outerField value: 10
}
}
출력 결과
Inner method with outerField value: 10
public class OuterClass {
private static int outerStaticField = 20;
// Static Nested Class
public static class StaticNestedClass {
public void staticMethod() {
System.out.println("Static method of StaticNestedClass with outerStaticField value: " + outerStaticField);
}
}
public static void main(String[] args) {
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
nested.staticMethod(); // Output: Static method of StaticNestedClass with outerStaticField value: 20
}
}
출력 결과
Static method of StaticNestedClass with outerStaticField value: 20
public class OuterClass {
public void outerMethod() {
int localVariable = 30;
// Method with Local Inner Class
class LocalInnerClass {
public void localMethod() {
System.out.println("Local method of LocalInnerClass with localVariable value: " + localVariable);
}
}
LocalInnerClass inner = new LocalInnerClass();
inner.localMethod(); // Output: Local method of LocalInnerClass with localVariable value: 30
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.outerMethod();
}
}
출력 결과
Local method of LocalInnerClass with localVariable value: 30
public class OuterClass {
public void anonymousMethod() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous inner class's run method");
}
};
new Thread(runnable).start();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.anonymousMethod(); // Output: Anonymous inner class's run method
}
}
출력 결과
Anonymous inner class's run method
내부 클래스는 주로 다음과 같은 곳에서 사용됨
위 에서 콜백 및 이벤트 처리에서 어떻게 사용되는지 예제 소스를 한번 보자.
// Interface for event listeners
interface OnClickListener {
void onClick();
}
public class Button {
private OnClickListener onClickListener;
// Method to register click event listener
public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
// Inner class representing click event handler
public class ClickHandler {
public void handleClick() {
if (onClickListener != null) {
onClickListener.onClick();
}
}
}
public void simulateClick() {
ClickHandler clickHandler = new ClickHandler();
clickHandler.handleClick();
}
public static void main(String[] args) {
Button button = new Button();
button.setOnClickListener(() -> System.out.println("Button clicked"));
button.simulateClick(); // Output: Button clicked
}
}
출력 결과
Button clicked
이렇게 내부 클래스를 사용하여 ClickHandler 클래스를 구현하여 setOnClickListener를 구현한다.
내부 클래스는 Java 프로그래밍에서 코드의 구조와 디자인을 개선하는 데 중요한 역할을 한다. 각각의 내부 클래스 유형(멤버 내부 클래스, 정적 내부 클래스, 지역 내부 클래스, 그리고 익명 내부 클래스)은 그 특성에 따라 다양한 상황에서 유용하게 사용된다. 이들은 코드의 캡슐화를 강화하고, 관련 기능을 근접하게 위치시켜 프로젝트의 구조를 더욱 명확하게 만든다. 또한, 테스트 가능성을 향상시키고, 코드의 재사용성을 높이며, 복잡한 이벤트 처리나 콜백 구현을 단순화한다.
내부 클래스를 사용할 때는 각 클래스의 범위와 연관성을 명확히 정의하는 것이 중요하다. 외부 클래스의 데이터에 강하게 의존하는 기능이나, 특정 메서드 내에서만 사용되는 클래스는 내부 클래스로 구현하여 외부에서의 불필요한 접근을 제한함으로써 보안성을 높이고, 코드를 깔끔하게 유지할 수 있다. 또한, 익명 내부 클래스를 활용하여 간단한 인터페이스나 추상 클래스의 구현을 인라인으로 제공함으로써 코드의 직관성을 개선할 수 있다.
결국, 내부 클래스는 Java 개발자의 도구 상자에서 매우 강력한 도구라고 할 수 있다. 적절히 활용한다면, 소프트웨어의 설계를 더욱 견고하고 유지보수하기 쉽게 만들어 줄 것 이다. 따라서, 각 프로젝트의 요구 사항과 문제에 맞게 내부 클래스를 전략적으로 사용하여, 보다 효율적인 프로그래밍을 해야한다.