[Day 14 | Java] 중첩 클래스

y♡ding·2024년 10월 31일
0

데브코스 TIL

목록 보기
93/163

중첩 클래스(Nested Class)는 다른 클래스 내부에 정의된 클래스입니다. 자바에서는 중첩 클래스를 통해 외부 클래스와 연관된 내부 클래스를 쉽게 정의하고, 클래스 간 관계를 보다 명확히 표현할 수 있습니다. 중첩 클래스는 크게 정적(static) 중첩 클래스와 비정적(non-static) 중첩 클래스(내부 클래스)로 나뉩니다.

종류구현 위치사용할 수 있는 외부 클래스 변수생성 방법
인스턴스 내부 클래스외부 클래스 멤버 변수와 동일외부 인스턴스 변수, 외부 전역 변수외부 클래스 인스턴스를 만든 후 생성
정적 내부 클래스외부 클래스 멤버 변수와 동일외부 전역 변수외부 클래스 인스턴스 없이도 생성 가능
지역 내부 클래스메서드 내부에 구현외부 인스턴스 변수, 외부 전역 변수, 메서드 변수메서드 호출 시 생성, 메서드 내에서만 사용 가능
익명 내부 클래스메서드 내부에 구현외부 인스턴스 변수, 외부 전역 변수선언과 동시에 생성

1. 인스턴스 내부 클래스

인스턴스 내부 클래스(Instance Inner Class)

  • 외부 클래스의 인스턴스와 함께 생성되는 클래스입니다.
  • 외부 클래스의 멤버 변수와 전역 변수에 모두 접근할 수 있습니다.
  • 외부 클래스의 인스턴스를 생성한 후에 인스턴스 멤버 클래스를 생성해야 하며, 외부 클래스와 밀접하게 연관된 기능을 수행할 때 유용합니다.

예시코드

package com.exam;

class Outer {
    private int x1 = 100; // private 접근 제어자 사용 - 외부 클래스에서 접근 불가
    public int x2 = 100;  // public 접근 제어자 사용 - 외부 클래스에서 접근 가능
    
      public Outer() {
        System.out.println("Outer : " + this);
    }

    // 내부 클래스 Inner 정의
    class Inner {
        private int y1 = 200; // private 접근 제어자 사용 - 외부 클래스에서 접근 불가
        public int y2 = 200;  // public 접근 제어자 사용 - 외부 클래스에서 접근 가능

        public Inner() {
            // Inner 객체가 생성되었을 때 메시지 출력
            System.out.println("Inner : " + this);
        }

        public void viewInner() {
            // 내부 클래스의 메서드 - 내부와 외부 클래스의 멤버 변수 출력
            System.out.println("Instance Member Class");
            System.out.println("x1 = " + x1); // 외부 클래스의 private 멤버 변수에 접근 가능
            System.out.println("x2 = " + x2); // 외부 클래스의 public 멤버 변수에 접근 가능
            System.out.println("y1 = " + y1); // 내부 클래스의 private 멤버 변수
            System.out.println("y2 = " + y2); // 내부 클래스의 public 멤버 변수
        }
    }
}

package com.exam;

public class InnerEx01 {
    public static void main(String[] args) {
        // Outer 클래스의 객체 생성
        Outer outer = new Outer();

        // Outer 클래스의 public 멤버 변수 접근 및 출력
        //System.out.println(outer.x1);  // private access
        System.out.println(outer.x2);

        // Outer 클래스의 Inner 인스턴스 멤버 클래스 객체 생성
        Outer.Inner inner = outer.new Inner();

        // Inner 클래스의 public 멤버 변수 접근 및 출력
        //System.out.println(inner.y1);    // private access
        System.out.println(inner.y2);

        // Inner 클래스의 메서드 호출하여 내부 정보 출력
        inner.viewInner();
    }
}
Outer : com.exam.Outer@6acbcfc0
100
Inner : com.exam.Outer$Inner@6f496d9f  // Outer - Inner 다른 참조값  
200
viewInner 호출
100
100
200
200

2. 정적 중첩 클래스

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

  • 외부 클래스의 인스턴스 없이 독립적으로 생성할 수 있는 클래스입니다.
  • 외부 클래스의 정적 변수 및 정적 메서드에만 접근할 수 있으며, 외부 클래스의 인스턴스 변수에는 접근할 수 없습니다.
  • 주로 외부 클래스와 독립적인 기능을 수행할 때 사용합니다.

예시2

package com.exam2;

public class Outer {
    private int x1 = 100; // private 접근 제어자 사용 - 외부 클래스에서 접근 불가
    public int x2 = 100;  // public 접근 제어자 사용 - 외부 클래스에서 접근 가능

    // 정적(static) 중첩 클래스 Inner 정의
    static class Inner {
        private int y1 = 200; // private 접근 제어자 사용 - 외부 클래스에서 접근 불가
        public int y2 = 200;  // public 접근 제어자 사용 - 외부 클래스에서 접근 가능

        public Inner() {
            // Inner 객체가 생성되었을 때 메시지 출력
            System.out.println("Inner : " + this);
        }

        void viewInner() {
            // Inner 클래스의 메서드 - static 클래스이므로 외부 클래스 인스턴스 변수 x1, x2에는 접근 불가
            System.out.println("viewInner 호출");
            // System.out.println("x1 = " + x1); // 접근 불가
            // System.out.println("x2 = " + x2); // 접근 불가
            System.out.println("y1 = " + y1); // 내부 클래스의 private 멤버 변수
            System.out.println("y2 = " + y2); // 내부 클래스의 public 멤버 변수
        }
    }
}

package com.exam2;

public class InnerEx01 {
    public static void main(String[] args) {
        // Outer 클래스의 정적(static) 중첩 클래스 Inner 객체 생성
        Outer.Inner inner = new Outer.Inner();

        // Inner 클래스의 public 멤버 변수 접근 및 출력
        //System.out.println(inner.y1); // private access
        System.out.println(inner.y2);

        // Inner 클래스의 메서드 호출하여 내부 정보 출력
        inner.viewInner();
    }
}

3. 지역 내부 클래스

지역 내부 클래스(Local Class)

  • 외부 클래스의 메서드 내부에서 정의된 클래스입니다. 메서드가 호출될 때 생성되며, 메서드가 종료되면 더 이상 사용할 수 없습니다.
  • 해당 메서드의 지역 변수와 외부 클래스의 변수에 접근할 수 있습니다.
  • 메서드 내부에서 특정한 작업을 수행할 때만 사용됩니다

예시코드

package com.exam3;

public class InnerEx {
    public static void main(String[] args) {
        int x = 200; // 지역 변수, effectively final (readonly)

        // 메서드 내부에 정의된 지역 클래스 Inner
        class Inner {
            private int y1 = 100; // private 접근 제어자 사용 - 외부에서 접근 불가
            public int y2 = 100;  // public 접근 제어자 사용 - 외부에서 접근 가능

            public Inner() {
                // Inner 객체가 생성되었을 때 메시지 출력
                System.out.println("Inner : " + this);
            }

            void viewInner() {
                // Inner 클래스의 메서드 - 내부 변수 출력
                System.out.println("viewInner 호출");
                System.out.println("y1 = " + y1); // 내부 클래스의 private 멤버 변수
                System.out.println("y2 = " + y2); // 내부 클래스의 public 멤버 변수
                
                System.out.println(x); // 지역변수 접근 가능 
                // x++; // 지역 변수는 effectively final이므로 변경 불가
            }
        }

        // 지역 클래스 Inner의 객체 생성 및 메서드 호출
        Inner inner = new Inner();
        inner.viewInner();
    }
}

effectively final에 대해

effectively final은 자바 8부터 추가된 개념으로, 명시적으로 final 키워드로 선언하지 않았지만, 사실상 값을 변경하지 않고 사용하는 지역 변수를 의미합니다.

  • x라는 지역 변수가 effectively final로 선언되어 있어, 지역 클래스에서도 접근할 수 있지만 값을 변경할 수는 없습니다.
  • effectively final 변수는 값을 변경하지 않기 때문에, 스레드 안전성이 증가하고 지역 클래스에서 변수를 참조할 때 안전하게 사용될 수 있습니다.

4. 익명 내부 클래스

익명 클래스(Anonymous Class)

  • 이름이 없는 클래스이며, 인터페이스나 추상 클래스를 구현하는 즉석 객체를 생성할 때 사용됩니다.
  • 주로 이벤트 핸들러와 같이 간단하게 필요한 기능을 한 번만 구현할 때 유용합니다.
  • new 키워드를 사용하여 선언과 동시에 인스턴스를 생성하며, 해당 인스턴스는 일회성으로 사용됩니다.
public interface InterA {
    // 인터페이스 메서드 선언 (추상 메서드)
    void viewInner();
}
public class InnerEx {
    public static void main(String[] args) {
        // 익명 이너 클래스 사용 (참조 변수 없음)
        // 인스턴스화와 동시에 인터페이스의 추상 메서드를 오버라이드하여 구현
        new InterA() {
            @Override
            public void viewInner() {
                System.out.println("viewInner 호출");
            }
        }.viewInner(); // 익명 객체로 viewInner 호출

        // 익명 이너 클래스 사용 (참조 변수 ia 사용)
        // 인터페이스 구현과 동시에 메서드 오버라이드
        InterA ia = new InterA() {
            @Override
            public void viewInner() {
                System.out.println("viewInner 호출");
            }
        };

        // 참조 변수를 통해 viewInner 호출
        ia.viewInner();
    }
}

package com.exam4;

public class InnerEx02 {
    private int y = 200; // 외부 클래스의 인스턴스 변수

    public void doInner() {
        int x = 100; // 지역 변수

        System.out.println("외부 this = " + this); // 외부 클래스의 this 출력

        // 익명 내부 클래스 정의와 동시에 viewInner 메서드 구현
        new InterA() {
            @Override
            public void viewInner() {
                System.out.println("viewInner 호출 ");
                
                // 내부 클래스의 this (익명 내부 클래스의 인스턴스 참조)
                System.out.println("내부 this = " + this);
                
                // 외부 클래스의 인스턴스를 가리키는 참조
                System.out.println("외부 this = " + InnerEx02.this);
                
                // 외부 클래스의 인스턴스 변수 y 출력
                System.out.println("y = " + InnerEx02.this.y);

                // System.out.println("y = " + this.y); // 컴파일 오류 발생
                // 오류 이유: 익명 내부 클래스에서 `this`는 익명 클래스 자신을 가리키므로 외부 클래스의 변수 y에 접근할 수 없음.
                // `this`를 사용해 외부 클래스 변수에 접근하려면 `OuterClassName.this` 형식을 사용해야 함 (InnerEx02.this.y)
            }
        }.viewInner(); // 익명 내부 클래스의 viewInner 메서드 호출
    }

    public static void main(String[] args) {
        // InnerEx02 클래스의 인스턴스 생성 후 doInner 메서드 호출
        InnerEx02 ie = new InnerEx02();
        ie.doInner();
    }
}

0개의 댓글

관련 채용 정보