3 minute read

item 24 멤버 클래스는 되도록 static으로 만들라

중첩 클래스

  • 다른 클래스 안에 정의된 클래스
  • 중첩 클래스는 자신을 감싸는 외부 클래스에서만 사용되어야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다.
  • 종류 : 정적 멤버 클래스, 멤버 클래스, 익명 클래스, 지역 클래스

정적 멤버 클래스

  • 바깥 클래스와 함께 쓰일 때 유용한 public 도우미 클래스로 쓰인다.
  • 클래스 내부에서 static으로 선언된 클래스이다.
  • 외부 클래스의 private 멤버에도 접근이 가능하다.
  • enum 타입도 암시적인 static으로 간주할 수 있다.
public class OuterClass {
    private static int outerStaticField = 10;

    public static class StaticMemberClass {
        private int innerField;

        public StaticMemberClass(int innerField) {
            this.innerField = innerField;
        }

        public void printFields() {
            System.out.println("Outer static field: " + outerStaticField);
            System.out.println("Inner field: " + innerField);
        }
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        OuterClass.StaticMemberClass staticMemberInstance = new OuterClass.StaticMemberClass(20);
        staticMemberInstance.printFields();
    }
}

	/* 출력 결과
Outer static field: 10
Inner field: 20
	*/

(비정적) 멤버 클래스

  • 정규화된 this(class.this)를 사용해 바깥 인스턴스의 메서드를 호출하거나 참조를 가져올 수 있다.
  • 외부 인스턴스 없이는 생성이 불가능하다. 따라서, 멤버 클래스에서 외부 인스턴스에 접근할 일이 없다면 무조건 static을 붙여 정적 멤버 클래스로 생성하자.
    • 외부 클래스와 멤버 클래스의 관계는 멤버 클래스가 인스턴스화될때 확립되며, 이후 더이상 변경할 수 없다.
  • 어댑터를 정의할 때 자주 쓰인다. (클래스의 view 구현, 다른 타입의 인스턴스 리턴)
public class OuterClass {
    private int outerField = 5;

    public class MemberClass {
        private int innerField;

        public MemberClass(int innerField) {
            this.innerField = innerField;
        }

        public void printFields() {
            System.out.println("Outer field: " + outerField);
            System.out.println("Inner field: " + innerField);
        }
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        OuterClass outerInstance = new OuterClass();
        OuterClass.MemberClass memberInstance = outerInstance.new MemberClass(15);
        memberInstance.printFields();
    }
}


/* 출력 결과

Outer field: 5
Inner field: 15

*/

익명 클래스

  • 이름이 없고, 외부 클래스의 멤버도 아니다.
  • 쓰이는 시점에 선언과 동시에 인스턴스가 생성되며, 어디서든 생성이 가능하다.
  • non-static한 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참고할 수 있다.
  • 이전에는 작은 함수 객체 혹은 처리 객체를 만들때 사용했다.(람다 나오고 나서는 잘 사용하지 않는 방식)
  • 정적 팩터리 메서드 구현시에도 사용한다.
interface MyInterface {  
    void myMethod();  
}  
  
public class OuterClass {  
    public MyInterface createAnonymousClass() {  
        return new MyInterface() {  
            @Override  
            public void myMethod() {  
                System.out.println("Anonymous class method implementation");  
            }  
        };  
    }  
}  
  
// 사용 예시  
class Main {  
    public static void main(String[] args) {  
        OuterClass outerInstance = new OuterClass();  
        MyInterface anonymousInstance = outerInstance.createAnonymousClass();  
        anonymousInstance.myMethod();  
    }  
}

/*  출력 결과
Anonymous class method implementation
*/

익명 클래스의 제약 사항

  • 선언한 지점에서만 인스턴스를 만들 수 있다.
  • instanceof 검사나 클래스의 이름이 필요한 작업은 수행할 수 없다.
  • 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출할 수 없다.
  • 여러 인터페이스를 구현할 수 없고, 인터페이스를 구현하는 동시에 다른 클래스를 상속할 수도 없다. (인터페이스의 이점이 사라진다.)
  • 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출할 수 없다.
  • 표현식이 중간에 등장하므로 코드가 길어지면 가독성이 떨어진다.

지역 클래스

  • 네 중첩 클래스 중 가장 사용 빈도가 낮다.
  • 지역 변수를 선언할 수 있다면 어디서든 선언할 수 있고, 유효 범위도 지역 변수와 같다.
  • 멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있다.
  • 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있다.
  • 정적 멤버는 가질 수 없으며 가독성을 위해 짧게 작성해야 한다.
public class OuterClass {  
    private int outerField = 7;  
  
    public void outerMethod() {  
        class LocalClass {  
            private int localField;  
  
            public LocalClass(int localField) {  
                this.localField = localField;  
            }  
  
            public void printFields() {  
                System.out.println("Outer field: " + outerField);  
                System.out.println("Local field: " + localField);  
            }  
        }  
  
        // 메서드 내에서 지역 클래스 사용  
        LocalClass localInstance = new LocalClass(14);  
        localInstance.printFields();  
    }  
}  
  
// 사용 예시  
class Main {  
    public static void main(String[] args) {  
        OuterClass outerInstance = new OuterClass();  
        outerInstance.outerMethod();  
    }  
}

/*  출력 결과

Outer field: 7
Local field: 14

*/

Leave a comment