ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템17. 변경 가능성을 최소화 하라.
    Java/이펙티브 자바 2023. 8. 15. 10:07

    핵심 정리 - 불변 클래스

    - 불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, 오류가 생길 여지도 적고 훨씬 안전하다.

    - 불변 클래스를 만드는 다섯 가지 규칙

        ㆍ객체의 상태를 변경하는 메서드를 제공하지 않는다.

              ▷ setter를 제공하지 않는다.

        ㆍ클래스를 확장할 수 없도록 한다

               상속을 할 수 없게 만든다. ( 1. final class 2. private contructor )

        ㆍ모든 필드에 final로 선언한다.

        ㆍ모든 필드를 private으로 선언한다.

               public이면 필드 참조를 할 수도 있기에 우리는 필드 참조를 원치않는다.

        ㆍ자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

               getter를 만들어 주지 않거나 방어적인 복사를 해서 사용

    public final class Person {
        private final Address address;  // address가 가진 정보들 까지 final이 아니다.
    
        public Person(Address address) {
            this.address = address;
        }
    
        public Address getAddress() {
    //        return address;
            Address copyOfAddress = new Address();
            copyOfAddress.setCity(address.getCity());
            copyOfAddress.setZipCode(address.getZipCode());
            copyOfAddress.setStreet(address.getStreet());
            return copyOfAddress;   // Person의 정보가 바뀌지 않게 된다.
        }
    }

     

    핵심 정리 - 불변 클래스의 장점과 단점

    - 함수형 프로그래밍에 적합하다. ( 피연산자에 함수를 적용한 결과를 반환하지만 피연산자가 바뀌지는 않는다. )

    - 불변 객체는 단순하다.

    - 불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.

    - 불변 객체는 안심하고 공유할 수 있다. ( 상수, public static final )

    - 불변 객체 끼리는 내부 데이터를 공유할 수 있다.

               ex) BigInteger의 negate

    - 객체를 만들 때 불변 객체로 구성하면 이점이 많다.

               ex) Set - 컬렉션들은 구성요소들 까지 같아야 같다.

    // 불변이라면
    Set<Integer> numbers = Set.of(1,2,3);
    
    // 불변이 아니라면
    final Set<Point> points = new HashSet<>();
    Point firstPoint = new Point(1, 2);
    points.add(firstPoint);
    firstPoint.x = 10;

    - 실패 원자성을 제공한다. ( 아이템 76, p407 )

               예외가 발생하더라도 파라미터나 필드가 바뀌지 않는다.

    - 단점) 값이 다르다면 반드시 별도의 객체로 만들어야 한다.

        ㆍ"다단계 연산"을 제공하거나, "가변 동반 클래스"를 제공하여 대처할 수 있다.

               가변동반클래스 ex) String

    public static void main(String[] args) {
        String name = "whiteship";
        StringBuilder nameBuilder = new StringBuilder(name);
        nameBuilder.append("phm");
    }

     

    핵심 정리 - 불변 클래스 만들 때 고려할 것

    - 상속을 막을 수 있는 또 다른 방법

        ㆍprivate 또는 package-private 생성자 + 정적 팩토리

        ㆍ확장이 가능하다. 다수의 package-private 구현 클래스를 만들 수 있다.

        ㆍ정적 팩터리를 통해 여러 구현 클래스중 하나를 활용할 수 있는 유연성을 제공하고 객체 캐싱 기능으로 성능을 향상 시킬 수도 있다.

    - 재정의가 가능한 클래스는 방어적인 복사를 사용해야 한다.

    // BigInteger은 상속을 막아두지 않았기에 방어적인 복사를 사용
    public static BigInteger safeInstance(BigInteger val) { 
        return val.getClass() == BigInteger.class ? val : new BigInteger(val.toByteArray());
    }

    - 모든 "외부에 공개하는" 필드가 final이어야 한다.

        ㆍ계산 비용이 큰 값은 해당 값이 필요로 할 때 (나중에) 계산하여 final이 아닌 필드에 캐시해서 쓸 수도 있다.

     

    완벽 공략 요약

    - p105, 새로 생성된 불변 인스턴스를 동기화 없이 다른 스레드로 건네도 문제없이 동작 (JLS 17.5)

    - p106, readObject 메서드 (아이템 88)에서 방어적 복사를 수행하라.

    - p112, 불변 클래스의 내부에 가변 객체를 참조하는 필드가 있다면... (아이템 88)

    - p113, java.util.concurrent 패키지의 CountDownLatch 클래스

     

    완벽 공략 - final 과 자바 메모리 모델(JMM)

    final을 사용하면 안전하게 초기화할 수 있다.

    - JMM과 final을 완벽히 이해하려면 JLS 17.4와 JLS 17.5를 참고하라

    - JMM

        ㆍ자바 메모리 모델은 JVM의 메모리 구조가 아니다.

        ㆍ적법한(legal) 프로그램을 실행 규칙

        ㆍ메모리 모델이 허용하는 범위내에서 프로그램을 어떻게 실행하든 구현체(JMM)의 자유이다.

           ( 이 과정에서 실행 순서가 바뀔 수도 있다. )

    - 어떤 인스턴스의 final 변수를 초기화 하기 전까지 해당 인스턴스를 참조하는 모든 스레드는 기다려야 한다.(freeze)

    class FinalFieldExample {
        final int x;
        int y;
        static FinalFieldExample f;
        
        public FinalFieldExample() {
            x = 3;
            y = 4;
        }
        
        static void writer() {
             f = new FinalFieldExample();
        }
        
        static void reader() {
            if (f != null) {
                int i = f.x;	// guaranteed to see 3
                int j = f.y;	// could see 0
        	}
        }
    }

               반드시 필드의 값이 설정되고 사용되길 바란다면 final 키워드를 추가한다.

     

    완벽 공략 - CountDownLatch

    java.util.concurrent 패키기

    병행(concurrency) 프로그래밍에 유용하게 사용할 수 있는 유틸리티 묶음

    - 병행(Concurrency)과 병렬(Parallelism)의 차이

    - 병행은 여러 작업을 번갈아 가며 실행해 마치 동시에 여러 작업을 동시에 처리하듯 보이지만, 실제로는 한번에 오직 한 작업만 실행한다. CPU가 한개여도 가능하다.

    - 병렬은 여러 작업을 동시에 처리한다. CPU가 여러개 있어야 가능하다.

    - 자바의 concurrent 패키지는 병행 애플리케이션에 유용한 다양한 툴을 제공한다.

        ㆍBlockingQueue, Callable, ConcurrentMap, Executor, ExecutorService, Future ...

     

    CountDownLatch

    다른 여러 스레드로 실행하는 여러 오퍼레이션이 마칠 때까지 기다릴 때 사용할 수 있는 유틸리티

    - 초기회 할 때 숫자를 입력하고, await() 메서드를 사용해서 숫자가 0이 될 때까지 기다린다.

    - 숫자를 셀 때는 countDown() 메서드를 사용한다.

    - 재사용할 수 있는 인스턴스가 아니다. 숫자를 리셋해서 재사용하려면 CyclicBarrier를 사용해야 한다.

    - 시작 또는 종료 신호로 사용할 수 있다.

     

     

     

     

    댓글

Designed by Tistory.