무지를 아는 것이 곧 앎의 시작

Java

멤버 클래스는 되도록 static으로

Alex96 2022. 3. 30. 02:15

이펙티브 자바의 아이템24. '멤버 클래스는 되도록 static으로 만들라' 부분을 읽고 정리하는 포스팅.✍️

중첩 클래스

다른 클래스 안에 정의된 클래스. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외에 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다.

중첩 클래스 종류

  1. 정적 멤버 클래스
  2. 멤버 클래스
  3. 익명 클래스
  4. 지역 클래스

정적 멤버 클래스를 제외하고는 모두 비정적이며 내부 클래스(inner class)라고 부른다.


정적 멤버 클래스와 비정적 멤버 클래스

일반 내부 클래스는 바깥 클래스의 private 멤버에 접근할 수 있지만, 정적 클래스는 바깥 클래스와 관계가 없어야 함으로 private 멤버에 접근할 수 없다.

정적 멤버 클래스

public class Member {
    private MemberSupport memberSupport;
    private String name;

    static class MemberSupport {
        void printName() {
            System.out.println(name); // 컴파일 에러
        }
    }
}

일반 내부 클래스 (멤버 클래스)

public class Member {
    private MemberSupport memberSupport;
    private String name;

    class MemberSupport {
        void printName() {
            System.out.println(name); // 허용되는 문법
        }
    }
}

정적 멤버 클래스의 접근 범위

정적 멤버 클래스는 다른 정적 멤버와 똑같은 접근 규칙을 적용 받는다.
ex) 바깥 클래스에서 private으로 선언하면 바깥 클래스에서만 접근이 가능하다.

정적 멤버 클래스는 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다.

계산기가 지원하는 연산 종류를 정의하는 열거 타입

public class Calculator {

    public static enum Operation {
        PLUS((number1, number2) -> number1 + number2),
        MINUS((number1, number2) -> number1 - number2),
        MULTIPLY((number1, number2) -> number1 * number2),
        DIVIDE((number1, number2) -> number1 / number2);

        private final BiFunction<Double, Double, Double> formula;

        Operation(BiFunction<Double, Double, Double> formula) {
            this.formula = formula;
        }

        public double calculate(double number1, double number2) {
            return formula.apply(number1, number2);
        }
    }
}

클라이언트에서 사용

public class Item24 {
    public static void main(String[] args) {
        System.out.println(Calculator.Operation.DIVIDE.calculate(10, 8)); // 1.25
    }
}

정적 멤버 클래스와 비정적 멤버 클래스의 차이

비 정적 멤버 클래스는 바깥 인스턴스와 암묵적으로 연결이 되어있다.

public class Member {
    private MemberSupport memberSupport;
    private String name;

    class MemberSupport {
        void printName() {
            Member member = Member.this;
            System.out.println(member.name);
        }
    }
}

바깥 클래스의 인스턴스를 클래스이름.this로 참조하여 사용할 수 있다.
반대로 정적 멤버 클래스는 바깥 클래스와 독립적으로도 사용할 수 있다. (위 계산기 예제)


익명 클래스

익명 클래스는 선언한 지점에서만 인스턴스를 만들 수 있다. intanceof검사나 클래스의 이름이 필요한 작업은 수행할 수 없다. 인터페이스를 구현할 수도 없고 다른 클래스를 상속할 수도 없다. 익명 클래스는 표현식 중간에 등장함으로 짧지 않으면 가독성이 떨어진다.

익명 클래스는 자바 8 이전에는 함수 객체를 표현하는 데에 사용하거나 처리 객체(process object)를 만드는데 사용했다.

익명 클래스로 함수객체를 표현

public static int sumAll(List<Integer> numbers, IntegerPredicate integerPredicate) {
    int total = 0;

    for (int number : numbers) {
        if (integerPredicate.test(number)) {
            total += number;
        }
    }

    return total;
}

public static int sumAllOverThree(List<Integer> numbers) {
    return sumAll(numbers, new IntegerPredicate() {
        @Override
        public boolean test(Integer number) {
            return number > 3;
        }
    });
}

지역 클래스

지역 클래스는 네 가지 중첩 클래스 중 가장 드물게 사용된다. 지역 변수를 선언할 수 있는 곳이면 어디든 선언 가능하다. 유효 범위도 지역변수와 같다.

다른 중첩 클래스들과의 공통점

  1. 멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있다.
  2. 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며, 정적 멤버를 가질 수 없다.
  3. 가독성을 위해 짧게 작성해야 한다.

결론.

메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다.

  • 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조X -> 정적 멤버 클래스
  • 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조O -> 비정적 멤버 클래스
  • 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한 곳이면서, 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 있다 -> 익명 클래스
  • 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한 곳인데 해당 타입으로 쓰기에 적합한게 없다. -> 지역 클래스

끗.

'Java' 카테고리의 다른 글

상태 패턴 정리  (0) 2022.04.12
타입 안전 이종 컨테이너  (0) 2022.04.05
Stream.forEach() vs for-each  (0) 2022.03.14
표준 함수형 인터페이스  (0) 2022.03.08
Collections.unmodifiableXX()는 방어적 복사가 아니다  (1) 2022.03.06