'김영한의 실전 자바 - 중급 1편' 강의를 들으면서 복습할만한 내용을 정리하였다.

7 중첩 클래스, 내부 클래스

7.1 중첩 클래스, 내부 클래스란?

중첩 클래스의 분류

중첩 클래스는 총 4가지가 있고, 크게 2가지로 분류할 수 있다.

  • 정적 중첩 클래스
  • 내부 클래스 종류
    • 내부 클래스
    • 지역 클래스
    • 익명 클래스

변수의 선언 위치

  • 정적 변수(클래스 변수)
  • 인스턴스 변수
  • 지역 변수

중첩 클래스의 선언 위치

  • 정적 중첩 클래스 -> 정적 변수와 같은 위치

  • 내부 클래스 -> 인스턴스 변수와 같은 위치

  • 지역 클래스 -> 지역 변수와 같은 위치

class Outer {
	...
 	//정적 중첩 클래스
 	static class StaticNested {
 		...
 	}
    
 	//내부 클래스
 	class Inner {
 		...
 	}
}
  • 정적 중첩 클래스는 정적 변수와 같이 앞에 static 이 붙어있다.

  • 내부 클래스는 인스턴스 변수와 같이 앞에 static 이 붙어있지 않다.

class Outer {
	
    public void process() {
 		//지역 변수
 		int lcoalVar = 0;
 
 		//지역 클래스
 		class Local {...}
 
 		Local local = new Local();
 	}
}
  • 지역 클래스는 지역 변수와 같이 코드 블럭 안에서 클래스를 정의한다.
  • 참고로 익명 클래스는 지역 클래스의 특별한 버전이다.

중첩 vs 내부

  • 중첩(Nested) : 어떤 다른 것이 내부에 위치하거나 포함되는 구조적인 관계

  • 내부(Inner) : 나의 내부에 있는 나를 구성하는 요소

7.2 정적 중첩 클래스

public class NestedOuter {

    private static int outClassValue = 3;
    private int outInstanceValue = 2;

    static class Nested {
        private int nestedInstanceValue = 1;

        public void print() {
            // 자신의 멤버에 접근
            System.out.println(nestedInstanceValue);

            // 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
            //System.out.println(outInstanceValue);

            // 바깥 클래스의 클래스 멤버에는 접근할 수 있다. private도 접근 가능
            System.out.println(outClassValue);
        }
    }
}
  • 정적 중첩 클래스 앞에는 static이 붙는다.

  • 바깥 클래스의 인스턴스에 소속되지 않는다.

    • 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.

    • 바깥 클래스의 클래스 멤버에는 접근할 수 있다.

      (다른 외부 클래스에서도 바깥 클래스의 클래스 멤버에 접근할 수 있지만, 정적 중첩 클래스는 바깥 클래스의 클래스 멤버의 접근 제어자가 private 인 경우에도 접근할 수 있다는 뜻)

정적 중첩 클래스 예시

public class NestedOuterMain {
    public static void main(String[] args) {
        NestedOuter outer = new NestedOuter();
        NestedOuter.Nested nested = new NestedOuter.Nested();
        nested.print();

        System.out.println("nested.getClass() = " + nested.getClass());

    }
}
  • 정적 중첩 클래스는 new 바깥클래스.중첩클래스() 로 생성할 수 있다.
  • 중첩 클래스는 NestedOuter.Nested 와 같이 바깥클래스.중첩클래스 로 접근할 수 있다.
  • 여기서 new NestedOuter() 로 만든 바깥 클래스의 인스턴스와 new NestedOuter.Nested() 로 만든 정적 중첩 클래스의 인스턴스는 서로 아무 관계가 없는 인스턴스이다. 단지 클래스 구조상 중첩해 두었을 뿐이다.
    • 참고로 둘이 아무런 관련이 없으므로 정적 중첩 클래스의 인스턴스만 따로 생성해도 된다.

인스턴스가 생성된 상태

바깥 클래스의 멤버에 접근

Nested.print() 를 살펴보자

정적 중첩 클래스는 바깥 클래스의 정적 필드에는 접근할 수 있다. 하지만 바깥 클래스가 만든 인스턴스 필드에는 바로 접근할 수 없다. 바깥 인스턴스의 참조가 없기 때문이다.

정리

정적 중첩 클래스는 사실 다른 클래스를 그냥 중첩해 둔 것일 뿐이다!! 쉽게 이야기해서 둘은 아무런 관계가 없다. 그냥 클래스 2개를 따로 만든 것에서 private 접근 제어자에 접근할 수 있다는 정도가 차이점이다.

정적 중첩 클래스의 활용

중첩 클래스(내부 클래스 포함)는 그 용도가 자신이 소속된 바깥 클래스 안에서 사용되는 것이다.

7.3 내부 클래스

public class InnerOuter {

    private static int outClassValue = 3;
    private int outInstanceValue = 2;

    class Inner {
        private int innerInstanceValue = 1;

        public void print() {
            // 자기 자신에 접근
            System.out.println(innerInstanceValue);

            // 외부 클래스의 인스턴스 멤버에 접근 가능, private 접근 가능
            System.out.println(outInstanceValue);

            // 외부 클래스의 클래스 멤버에 접근 가능, private 접근 가능
            System.out.println(outClassValue);
        }
    }
}
  • 내부 클래스 앞에는 static이 붙지 않는다.

  • 바깥 클래스의 인스턴스에 소속된다.

    • 바깥 클래스의 인스턴스 멤버에 접근할 수 있다.
    • 바깥 클래스의 클래스 멤버에 접근할 수 있다.

내부 클래스 예시

public class InnerOuterMain {
    public static void main(String[] args) {
        InnerOuter outer = new InnerOuter();
        InnerOuter.Inner inner = outer.new Inner();
        inner.print();

        System.out.println("inner.getClass() = " + inner.getClass());
    }
}
  • 내부 클래스는 바깥 클래스의 인스턴스에 소속된다. 따라서 바깥 클래스의 인스턴스 정보를 알아야 생성할 수 있다.

  • 내부 클래스는 new 바깥클래스의 인스턴스 참조.내부클래스() 로 생성할 수 있다.

    • 내부 클래스는 바깥 클래스의 인스턴스에 소속되어야 한다. 따라서 내부 클래스를 생성할 때, 바깥 클래스의 인스턴스 참조가 필요하다.

    • outer.new Inner() 에서 outer 는 바깥 클래스의 인스턴스 참조를 가진다.

  • outer.new Inner() 로 생성한 내부 클래스는 개념상 바깥 클래스의 인스턴스 내부에 생성된다.

  • 따라서 바깥 클래스의 인스턴스를 먼저 생성해야 내부 클래스의 인스턴스를 생성할 수 있다.

개념 - 내부 클래스의 생성

  • 개념상 바깥 클래스의 인스턴스 내부에서 내부 클래스의 인스턴스가 생성된다.

  • 따라서 내부 인스턴스는 바깥 인스턴스를 알기 때문에 바깥 인스턴스의 멤버에 접근할 수 있다.

실제 - 내부 클래스의 생성

  • 실제로 내부 인스턴스가 바깥 인스턴스 안에 생성되는 것은 아니다. 하지만 개념상 인스턴스 안에 생성된다고 이해하면 충분하다.

  • 실제 내부 인스턴스는 바깥 인스턴스의 참조를 보관한다. 이 참조를 통해 바깥 인스턴스의 멤버에 접근할 수 있다.

7.4 중첩 클래스를 사용하는 이유

  • 논리적 그룹화 : 특정 클래스가 다른 하나의 클래스 안에서만 사용되는 경우 해당 클래스 안에 포함하는 것이 논리적으로 더 그룹화 된다. 패키지를 열었을 때 다른 곳에서 사용될 필요가 없는 중첩 클래스가 외부에 노출되지 않는 장점도 있다.

  • 캡슐화 : 중첩 클래스는 바깥 클래스의 private 멤버에 접근할 수 있다. 이렇게 해서 둘을 긴밀하게 연결하고 불필요한 public 메서드를 제거할 수 있다. 이는 곧 캡슐화를 높인다고 할 수 있다.

같은 이름의 바깥 변수 접근

변수의 이름이 같기 때문에 어떤 변수를 먼저 사용할지 우선 순위가 필요하다.
프로그래밍에서 우선 순위는 대부분 더 가깝거나, 더 구체적인 것이 우선권을 가진다. 쉽게 이야기해서 사람이 직관적으로 이해하기 쉬운 방향으로 우선 순위를 설계한다.

프로그래밍에서 가장 중요한 것은 명확성이다. 이름이 같은 경우 처음부터 이름을 서로 다르게 지어서 명확하게 구분하는 것이 더 나은 방법이다.


profile
가오리의 개발 이야기

0개의 댓글