자동화 구축 작업을 진행하면서 Immutable, Mutable 특성 및 내부적으로 데이터를 저장하는 과정을 한번 정리할 필요가 있을 것으로 생각하여 정리한다.
자바에서 참조형 변수(객체)는 힙 영역에 할당되고, 스택 영역에 지역변수나 스레드(실행을 유발하는 동작/메소드)가 할당된다.
예를 들어 아래와 같이 변수를 선언했다고 하자.
Integer i = 1;
i = 3;
힙 영역에 실제 데이터인 1이 할당되고 이를 참조변수 i가 스택 영역에서 참조받는데, 3을 할당하면 객체의 값이 변경된 것이 아니라, 힙 영역에 새로운 객체를 생성하고 이 객체에 대한 참조값을 변경한다.
public final class Integer extends Number implements Comparable<Integer> {
private final int value;
...
}
그리고 기존 1을 가진 주소값은 gc에 의해 사라지게 되며, 이처럼 스택의 데이터값을 직접적으로 "수정"할 수 없고 새로운 객체 혹은 데이터를 생성하여 해당 데이터를 참조하는 "참조주소를 바꾸는" 방식이 발생하는 경우를 Immutable이라 한다.
참고로 위 Interger 변수 할당 방식을 보면 알 수 있듯이 final로 선언하므로 할당된 변수의 "값"을 바꿀 수는 없다.
이번에 리팩토링하면서 다시 한번 정리했던 개념이 바로 이 부분이다.
poplntTermEngName
.replaceAll("baljeonso", " Plant ")
.replaceAll("Baljeonso", " Plant ")
.replaceAll("taeyanggwang", " Solar Power ")
.replaceAll("Taeyanggwang", " Solar Power ")
.replaceAll("Baiomaeseu", " Bioelectric Power ")
.replaceAll("baiomaeseu", " Bioelectric Power ")
.replaceAll("baio", " Bioelectric Power ")
.replaceAll("Baio", " Bioelectric Power ")
.replaceAll("pungnyeok", " Wind Power ")
.replaceAll("Pungnyeok", " Wind Power ");
위와 같이 poplntTermEngName 이라는 변수에 할당된 데이터를 replaceAll하여 바꾼다고 할 때, poplntTermEngName이 String으로 선언되어있어 해당 데이터를 직접적으로 변경하는 것은 불가능하였다.
따라서 아래와 같이
poplntTermEngName = poplntTermEngName
.replaceAll("baljeonso", " Plant ")
.replaceAll("Baljeonso", " Plant ")
.replaceAll("taeyanggwang", " Solar Power ")
.replaceAll("Taeyanggwang", " Solar Power ")
.replaceAll("Baiomaeseu", " Bioelectric Power ")
.replaceAll("baiomaeseu", " Bioelectric Power ")
.replaceAll("baio", " Bioelectric Power ")
.replaceAll("Baio", " Bioelectric Power ")
.replaceAll("pungnyeok", " Wind Power ")
.replaceAll("Pungnyeok", " Wind Power ");
새로운 데이터(객체)를 "다시 바라보게 해주어야", 즉 "재할당 해야" 비로소 바뀐 객체의 값을 바라보게 된다.
기존의 객체는 더이상 바라보지 않고 gc에 의해 제거된다.
기본적으로 ==은 힙에 생성된 데이터, 즉 객체를 동일하게 참조하고 있느냐 다시 말해 "동일한 객체이냐"를 검증한다.
이와 달리 equals는 할당된 변수값만 비교한다, 즉 객체가 달라도 내용이 같으면 같은 것으로 간주한다.
이때 String, Integer 등은 immutable하기 때문에 값이 바뀌지 않으므로, ==으로 엄격하게 객체값을 비교할 필요 없이 .equals()로 비교해도 무방하며 안전한 비교를 보장할 수 있다.
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
더 나아가 보자.
기본형(primitive) 데이터를 자동으로 래퍼 클래스(wrapper class) 로 변환해주는 것
즉, 자바가 int ↔ Integer 같은 변환을 자동으로 해주는 기능을 오토박싱(-> Wrapper)/언박싱(-> Primitive)이라 한다.
예를 들어
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // ✅ true
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // ❌ false
자바는 -128 ~ 127 범위의 Integer 값은 캐시(cache)하는 특성이 있기에, 해당 범위의 값은 같은 객체(같은 주소)를 재사용해서 ==도 true가 되지만, 그 외의 값은 새 객체 생성이기 때문에 ==는 false로 나타난다.
따라서 List에 들어가는 값을 오토박싱하는 자바의 특성상 반드시 equals로 "내용비교"를 해주어야 예상한 결과를 얻을 수 있다.
이처럼 Immutable한 변수 할당은 결국 재할당이 필요하므로, Call By Value가 주를 이루는 로직에서 유의하며 활용해야 할 것이다.
같이 공부하면서 정리 - https://velog.io/@gyrbs22/Immutable