JAVA : InnerClass

keymu·2024년 9월 30일
0

Inner Class(내부 클래스)
1. Member inner class(멤버 내부 클래스): 다른 클래스 내부에서 선언된 클래스
2. Static inner class(static 내부 클래스): 다른 클래스의 내부에서 static으로 선언된 클래스
3. Local class(지역 클래스)

  • Local inner class(지역 내부 클래스): 메소드 내부에서 선언된 클래스
  • Anonymous inner class(익명 내부 클래스): 이름이 없는 local class
public class Inner01Main {

    public static void main(String[] args) {
       System.out.println("Member Inner Class(멤버 내부 클래스)");

       // 외부 클래스의 인스턴스 생성
       TestOuter out = new TestOuter(100);

       // 멤버 내부 클래스의 인스턴스 생성
       // 멤버 내부 클래스의 이름: [외부클래스 이름].[멤버 내부클래스 이름]
       // [외부클래스 이름].[내부클래스 이름] 참조변수 =
       //      [외부클래스 인스턴스].new 내부클래스 생성자();
       TestOuter.TestInner in=out.new TestInner(111);
       in.printOuterValue(); //100
       in.printInnerValue(); //111

       // 하나의 외부 클래스 인스턴스를 이용해서
       // 멤버 내부 클래스의 인스턴스는 여러개를 생성할 수 있다.
       System.out.println();
       TestOuter.TestInner in2 = out.new TestInner(123);
       in2.printOuterValue(); //마찬가지로 동일한 out 쓰는 중 : 100
       in2.printInnerValue(); //123

       System.out.println();
       TestOuter.TestInner in7;
       in7 = new TestOuter(30).new TestInner(330);
       in7.printOuterValue(); //30
       in7.printInnerValue(); //330

    } // end main()

} // end class Inner01Main

1. Member inner class(멤버 내부 클래스)

  • TestOuter 클래스 내부에서 TestInner 클래스를 정의
    • 외부 클래스(Outer Class, Enclosing Class)
  • TestInner: 멤버 내부 클래스(Member Inner Class)
    1) 멤버 내부 클래스는 외부 클래스의 인스턴스가 생성된 이후에야 인스턴스 생성이 가능함.
    2) 멤버 내부 클래스는 외부 클래스의 모든 멤버들(private 포함)을 사용 가능
// 클래스: 멤버 변수들 (+ 생성자들) + 멤버 메소드들 = 데이터 타입
public class TestOuter {
    // 멤버 변수:
    private int outerValue;

    // 생성자:
    public TestOuter() {
    }

    public TestOuter(int outerValue) {
        this.outerValue = outerValue;
    }

    // 메소드:
    public int getValue() {
        return outerValue;
    }

    public void setValue(int outerValue) {
        this.outerValue = outerValue;
    }

    // Inner class 정의
    // (수식어) class 클래스이름 {...}
    public class TestInner {
        //필드
        private int innerValue;

        //생성자
        public TestInner() {
        }

        public TestInner(int innerValue) {
            this.innerValue = innerValue;
        }

        //메소드
        public void printOuterValue() {
            //내부 클래스는 외부 클래스의 필드 직접 접근 가능
            System.out.println("outerValue: " + outerValue);
        }
        public void printInnerValue() {
            System.out.println("innerValue: " + innerValue);
        }
    }//end inner class


} // end class TestOuter

InnerClass는 왜 쓰는가?

상속 관계로 묶을 수는 없지만, A라는 객체가 생성된 이후에야 존재할 수 있는 B라는 객체가 있다고 한다면, B를 A의 멤버 '내부 클래스'로 선언한다.
ex) 컴퓨터-CPU / 메모리, 자동차-타이어

반면, '햄버거 세트 메뉴' 객체의 경우, 햄버거 객체와 콜라 객체는 별개의 객체로도 존재 가능하니까 '햄버거' 와 '콜라' 는 '세트메뉴' 객체의 '멤버변수'로 붙도록 하는게 낫다.

is-a : 상속관계
has-a (종속) : 멤버내부클래스
has-a (독립) : 멤버변수


만약 외부변수, 내부클래스 내 변수, 지역변수의 변수이름이 모두 같다면?

2. Nested Class(중첩 클래스):

다른 클래스의 내부에서 멤버로 정의된 클래스인데, static 키워드가 사용된 내부 클래스 (static inner class)

static:

  • 클래스의 인스턴스가 "생성되지 않아도" 사용될 수 있는 멤버(변수, 메소드)에 사용
  • nested class는 외부 클래스의 인스턴스를 생성하지 않고, 내부 클래스의 인스턴스를 생성할 수 있다.
  • nested(static) class는 (외부 클래스에서) static으로 선언된 변수와 메소드만 사용 가능

중첩 클래스의 인스턴스 생성:

  • 타입 참조변수 = new 생성자()
  • 중첩 클래스의 이름(타입): [외부클래스 이름].[내부클래스 이름]
  • 중첩 클래스의 생성자: new [외부클래스 이름].생성자()

3. Local Inner Class:

  • 블록({ ... }) 내부에서 정의된 클래스
    1. 정의가 된 블록(메소드) 내부에서만 사용 가능 - 참조변수 선언, 인스턴스 생성
    2. 접근 수식어(public, protected, private)는 쓸 수 없다.
    3. 외부 클래스의 멤버 변수(private 포함)는 모두 사용 가능
    4. effectively final인 지역변수나 매개변수만 사용 가능

effectively final 변수란?
1. final로 선언된 변수, 또는
2. 한 번 초기화가 된 이후로 값이 변경되지 않은 변수(Java 8에서 도입)

public class Local01Main {

    public static void main(String[] args) {
       System.out.println("Local Inner Class(지역 내부 클래스)");

       TestOuter out=new TestOuter();
       out.localMethod(400);
       
    } // end main()

} // end class Local01Main
public class TestOuter {

    //TestOuter 클래스의 멤버 변수
    private int num1 = 100; // ①

    // TestOuter 클래스의 멤버 메소드
    public void localMethod(final int num4) {
        int num2 = 200; //localMethod() 지역변수

//        num4 *= 2; //오류: final임
        // LocalMethod 메소드 안에 정의된 Local inner class
        class TestLocal {
            private int num3 = 300; // Local class의 필드

            //class에 접근제어자 못 붙임. 내부는 괜찮음
            public void showNumbers() {
                System.out.println("num1 = " + num1);
                System.out.println("num2 = " + num2);
                System.out.println("num3 = " + num3);
                System.out.println("num4 = " + num4);
            }
        }
        TestLocal local = new TestLocal();
        //num2 = 400;
        // num2 값을 변경하면.. 아래 showNumbers()에선
        // 200 이 찍혀야 하나? 400이 찍혀야 하나?
        // 그래서 로컬내부클래스에서 사용 가능한 지역의 변수는
        // 반드시 effectively final 이어야 한다
        //       즉 한번 초기화 된후 값이 변경되지 않거나, final 이어야 한다.

        local.showNumbers();

    } // end localMethod()


} // end class TestOuter
profile
Junior Backend Developer

0개의 댓글