[Java ☕️] 불변 객체 :: 사이드 이펙트 나가!

ConewLog·2024년 7월 26일

Java ☕️

목록 보기
2/7

1. 사이드 이펙트란?

코드가 의도하지 않은 결과를 가져오는 것을 말한다.
객체 A의 상태를 변경하는 코드를 작성했는데, 이 코드가 객체 B의 상태도 변경시키는 경우, 이는 사이드이펙트이다.


2. 참조형 변수와 사이드 이펙트

java data type

  • primitive type / value type (원시 타입/값 타입)

    • 실제 값을 저장하는 타입이다.
    • boolean, byte, short, int, long, char, float, double
    • 값이 메모리의 stack에 저장된다.
    • 각 변수는 고유한 메모리 주소를 가지고, 그 주소에는 값이 저장된다.
  • reference type (참조 타입)

    • 동적으로 할당된 객체의 메모리 주소(참조)를 저장하는 타입이다.
    • 클래스, 배열, 인터페이스 ...
    • 값이 메모리의 heap에 저장된다.
    • 객체는 생성될 때 heap에 저장되고, 각 변수는 객체가 저장된 메모리 주소를 저장한다.

참조형 변수의 사이드 이펙트 예시

// Student.class
public class Student {
    private String name;
    private String major;

    public Student(String name, String major) {
        this.name = name;
        this.major = major;
    }

    public void setMajor(String major) {
        this.major = major;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", major='" + major + '\'' +
                '}';
    }
}
// StudentMain.class
public class StudentMain {
    public static void main(String[] args) {
        Student studentA = new Student("코뉴", "Computer Science");
        Student studentB = studentA;

        System.out.println("studentA와 studentB는 \"코뉴\" 라는 이름을 가진 동명이인이고, 둘 다 컴퓨터과학 전공이다.");
        System.out.println("studentA = " + studentA);
        System.out.println("studentB = " + studentB);

        System.out.println("\n어느날, studentB인 코뉴는 수학과로 전과한다.");
        studentB.setMajor("Mathematics");
        System.out.println("studentA = " + studentA);
        System.out.println("studentB = " + studentB);
        System.out.println("‼️studentA인 코뉴까지 전산에서 전공이 바뀌었다!");
    }
}
// 출력 결과
studentA와 studentB는 "코뉴" 라는 이름을 가진 동명이인이고, 둘 다 컴퓨터과학 전공이다.
studentA = Student{name='코뉴', major='Computer Science'}
studentB = Student{name='코뉴', major='Computer Science'}

어느날, studentB인 코뉴는 수학과로 전과한다.
studentA = Student{name='코뉴', major='Mathematics'}
studentB = Student{name='코뉴', major='Mathematics'}
‼️studentA인 코뉴까지 전산에서 전공이 바뀌었다!
  • StudentAStudentB가 같은 객체를 가리키고 있었기 때문에 발생하는 문제이다.

3. 참조형 변수의 공유 문제를 해결하는 방법

불변 객체 (Immutable Class)

String, Integer, LocalDate 등이 불변 객체로 설계되어 있다.

  • 멤버 변수를 final로 선언한다.
    • final 키워드가 붙어있으면, 변수의 값을 수정할 수 없다.
  • 멤버 변수의 변경이 필요하다면, 새로운 객체를 리턴한다.
//ImmutableStudent.class
public class ImmutableStudent {

    private final String name;
    private final String major;

    public ImmutableStudent(String name, String major) {
        this.name = name;
        this.major = major;
    }

    public ImmutableStudent withMajor(String major) {
        return new ImmutableStudent(name, major);
    }

    @Override
    public String toString() {
        return "ImmutableStudent{" +
                "name='" + name + '\'' +
                ", major='" + major + '\'' +
                '}';
    }
}
// ImmutableStudentMain.class
public class ImmutableStudentMain {
    public static void main(String[] args) {
        ImmutableStudent studentA = new ImmutableStudent("코뉴", "ComputerScience");
        ImmutableStudent studentB = studentA;

        System.out.println("studentA와 studentB는 \"코뉴\" 라는 이름을 가진 동명이인이고, 둘 다 컴퓨터과학 전공이다.");
        System.out.println("studentA = " + studentA);
        System.out.println("studentB = " + studentB);

        System.out.println("\n어느날, studentB인 코뉴는 수학과로 전과한다.");
        studentB = studentB.withMajor("Mathematics");
        System.out.println("studentA = " + studentA);
        System.out.println("studentB = " + studentB);
    }
}
// 출력 결과
studentA와 studentB는 "코뉴" 라는 이름을 가진 동명이인이고, 둘 다 컴퓨터과학 전공이다.
studentA = ImmutableStudent{name='코뉴', major='ComputerScience'}
studentB = ImmutableStudent{name='코뉴', major='ComputerScience'}

어느날, studentB인 코뉴는 수학과로 전과한다.
studentA = ImmutableStudent{name='코뉴', major='ComputerScience'}
studentB = ImmutableStudent{name='코뉴', major='Mathematics'}
  • 멤버 변수 name, majorfinal 키워드를 사용해 값 수정을 불가능하게 했다.
  • withMajor 메서드를 생성하여 major 값 변경이 필요한 경우, 새로운 객체를 리턴하도록 했다.

참고 사이트

profile
코뉴로그

0개의 댓글