J-S-12 중첩 클래스부터 익명 객체

글 정보
카테고리
Programming/Java/Starter
태그
JavaLevel1

1. 중첩 클래스 (Nested Class)

개요 및 정의

객체화가 필요한 대상이 존재하지만, 그 사용 범위가 특정 클래스 내부로 제한될 때 사용합니다.

클래스 내부에 또 다른 클래스를 선언하는 형태입니다.

설계 목적

논리적 그룹화 (Logical Grouping)

캡슐화 (Encapsulation)

종류

1. 정적 중첩 클래스 (Static Nested Class)

2. 비정적 중첩 클래스 (Non-static / Inner Class)


2. 정적 중첩 클래스 (Static Nested Class)

특징

외부 클래스의 인스턴스 없이도 생성할 수 있습니다.

외부 클래스의 private 정적 멤버(static member)에는 접근할 수 있습니다.

하지만 외부 클래스의 인스턴스 멤버에는 접근할 수 없습니다.

호출 방법

선언 시 클래스 내부에 static 키워드를 붙여 정의합니다.

호출 시에는 온점(.)을 사용하여 외부클래스.내부클래스 형태로 접근하는 것이 핵심입니다.

코드 예시

public class Outer {
    private static String staticData = "공용 데이터";
    private String instanceData = "개별 데이터";

    // static 키워드를 붙여 정의합니다.
    public static class StaticNested {
        public void print() {
            // 1. 외부의 private static 멤버에는 접근 가능합니다.
            System.out.println("접근 가능: " + staticData);

            // 2. 외부의 인스턴스 멤버에는 접근할 수 없습니다. (컴파일 에러 발생)
            // System.out.println(instanceData); 
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 3. 외부 클래스의 객체(new Outer())를 만들지 않아도 바로 생성할 수 있습니다.
        // 문법: new 외부클래스.내부클래스();
        Outer.StaticNested nested = new Outer.StaticNested();
        
        nested.print();
    }
}

3. 내부 클래스 (Inner Class)

특징

외부 클래스의 인스턴스에 종속된 클래스입니다.

외부 클래스의 private 멤버를 포함한 모든 멤버에 자유롭게 접근할 수 있습니다.

참조 및 생성 방법

내부 클래스 안에서 외부 클래스의 인스턴스를 명시적으로 가리킬 때는 외부클래스명.this 형식을 사용합니다.

생성 방식이 다소 독특합니다.

코드 예시

public class Outer {
    private String name = "외부 클래스 데이터";

    public class Inner {
        private String name = "내부 클래스 데이터";

        public void print() {
            // 1. 자신의 멤버 접근 (this 생략 가능)
            System.out.println("Inner: " + this.name);

            // 2. 외부 클래스의 멤버 접근 (Outer.this 문법 사용)
            // 이름이 겹칠 때 명시적으로 외부 인스턴스를 가리키는 방법입니다.
            System.out.println("Outer: " + Outer.this.name);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 1. 외부 클래스의 인스턴스를 먼저 생성해야 합니다.
        Outer outer = new Outer();

        // 2. 생성된 외부 객체(outer)를 통해 내부 객체를 생성합니다.
        // 문법: 참조변수.new 내부클래스();
        Outer.Inner inner = outer.new Inner();
        
        inner.print();
    }
}

4. 지역 클래스 (Local Class)

정의

메서드 바디(블록) 내부에 정의하는 클래스입니다.

특징 및 수명 문제

익명 객체의 원리를 이해하기 위한 필수 이론입니다.

메서드 내부의 지역 변수에 접근할 수 있습니다.

[변수 캡처링 (Variable Capturing)]

코드 예시

public class LocalClassExample {
    public void execute() {
        // 1. 지역 변수 (Stack 영역): 메서드가 끝나면 사라질 운명입니다.
        int localVar = 100; 

        // 2. 지역 클래스 선언: 메서드 블록({}) 내부에서만 존재합니다.
        class LocalPrinter {
            public void print() {
                // 3. 변수 캡처링 (Variable Capturing)
                // 사라질 localVar의 값을 객체 내부로 복사해옵니다.
                // 따라서 localVar는 변경되지 않는 'final' 상태여야 합니다.
                System.out.println("Captured Value: " + localVar);
            }
        }

        // 객체 생성 및 메서드 호출
        LocalPrinter printer = new LocalPrinter();
        printer.print();
    }
}

5. 내부 인터페이스 (Inner Interface)

개요

클래스 내부에 interface를 선언하는 형태입니다.

주로 정적(static) 멤버 인터페이스로 활용됩니다.

활용 예시: 이벤트 리스너

GUI 개발(Android, Swing 등)에서 이벤트 처리를 규격화하기 위해 자주 사용됩니다.

사용자의 클릭(Click) 등의 이벤트를 감지하고, 이에 대응하는 핸들러(Handler) 코드를 작성할 때 일정한 규격을 제공합니다.

코드 예시

public class Button {
    // 1. 내부 인터페이스 선언 (규격 정의)
    // 클래스 내부에 선언된 인터페이스는 자동으로 static으로 간주됩니다.
    public interface OnClickListener {
        void onClick();
    }

    private OnClickListener listener;

    // 2. 외부에서 구현한 동작(Handler)을 받아옵니다.
    public void setListener(OnClickListener listener) {
        this.listener = listener;
    }

    // 버튼이 눌렸을 때, 등록된 동작을 실행합니다.
    public void touch() {
        if (listener != null) {
            listener.onClick();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Button btn = new Button();

        // 3. 인터페이스 구현 및 주입
        // "클릭되면 무슨 일을 할지"를 여기서 결정합니다.
        btn.setListener(new Button.OnClickListener() {
            @Override
            public void onClick() {
                System.out.println("버튼이 클릭되었습니다!");
            }
        });

        btn.touch(); // 실행 결과: 버튼이 클릭되었습니다!
    }
}

6. 익명 객체 (Anonymous Object)

정의

이름이 없는 지역 클래스(Local Class)입니다.

특정 클래스를 상속받거나, 인터페이스를 구현하는 객체를 선언과 동시에 생성합니다.

목적 및 장점

클래스를 별도로 정의하기에는 재사용성이 없고, 딱 한 번만 사용할 때 유용합니다.

하나의 구문 안에 클래스 정의와 생성이 포함되어 코드가 간결해집니다.

GUI 프로그래밍에서 이벤트 처리 코드를 획기적으로 줄여줍니다.

특징

이름이 없기 때문에 생성자(Constructor)를 가질 수 없습니다.

코드 예시

public class AnonymousExample {
    // 예제를 위한 간단한 인터페이스
    interface Greeter {
        void sayHello();
    }

    public static void main(String[] args) {
        // 1. 클래스 이름 없이, 인터페이스를 구현하는 동시에 객체를 생성합니다.
        // 문법: new 인터페이스이름() { ... 구현 내용 ... };
        Greeter guest = new Greeter() {
            @Override
            public void sayHello() {
                System.out.println("안녕하세요! 저는 이름 없는 객체입니다.");
            }
        };

        // 2. 생성된 객체 사용
        guest.sayHello();
    }
}