Programming/JAVA

[Effective Java] 불변 클래스 (Immutable class)

코딩의성지 2023. 2. 7. 02:01

불변 클래스 (Immutable class) 는 말 그대로 인스턴스 내부의 값을 수정할 수 없는 클래스다. 클래스를 불변으로 만들면 가변 클래스보다 훨씬 설계하고 구현하고 사용하기 쉽고, 오류없이 안전하게 사용이 가능하다.

 

불변 클래스 (Immutable class) 는 Thread safe 해야하고, 예외가 발생해도 유효한 상태를 유지해야하며, 값이 다르다면 무조건 독립적인 객체로 생성되어야한다.

 

불변 클래스 (Immutable class) 를 만들기 위해서는 아래 다섯가지 특징을 따라야 한다.

 

1. 상태를 변경하는 메서드 (setter 메서드 등) 을 제공하지 않는다.

2. Class 가 확장하지 않도록 한다 (final class)

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

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

5. 자신을 제외하고는 가변 컴포넌트에 접근 불가하도록 만든다.

 

이와 관련한 예시를 하나 들어보도록 하겠다.

class Player {
    @Getter
    private String name;
    @Getter @Setter
    private String position;
    @Getter @Setter
    private int overall; 
}

@AllArgsConstructor
final class Team {
    @Getter @Setter
    private final String teamName;
    private final List<Player> playerList;
    
    public List<String> getPlayerList() {
    	return playerList.stream().map(PayerInfo::getName).collect(Collectors.toList());
    }
}

위의 예시에서 Team 클래스를 final로 하고 필드들도 private final을 선언했음에도 불구하고 playerList에 Player가 가변 컴포넌트라 해당 객체는 변경될 우려가 있다. 그래서 해당 필드에 final을 선언했고, 변경될 우려가 있는 값을 가져오지 못하도록 별도의 getter 메서드 대신에 필요한 정보만 불변으로 변경해서 가져오도록 메서드를 만들었다. 이를 통해 가변 컴포넌트에 접근 자체가 불가능하도록 만들어 버렸다. 그리고 추가적으로 Setter가 필요한 필드에만 어노테이션을 추가해줬다.

 

이렇게 사용해주면 우리는 완벽하게 불변 클래스를 만들어 사용한 것이 된다.

 

실제로 RESTFul API를 개발할때 우리는 요청하고 응답하는 객체를 Entity, VO나 DTO 객체 등으 Value Object로 분리해서 사용하곤 하는데 실무에서는 불변 클래스를 이용해서 아래와 같이 불변성을 강제하기도 한다.

@Entity
@Setter
@Getter
public class Member {
    @Id
    private Long id;
    private String name;
    private int age;
}
@Getter
public class MemberDto {
    private final String name;
    private final int age;
    private MemberDto(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public static final MemberDto from (Member m) {
    	return new MemberDto(m.getName(), m.getAge());
    }
}

 

모든 클래스가 불변이긴 힘들지만 최대한 변경할 수 있는 부분을 최대한 줄일 수 있으면 줄이려는 노력을 해보자

반응형