기본형과 참조형의 공유
자바의 데이터타입은 기본형(Primitive Type), 참조형 (Reference Type)으로 나눌 수 있다.
기본형: 하나의 값을 여러 변수에서 절대 공유하지 않음
int a = 10;
int b = a;
b = 20;
// a = 10 , b = 20
참조형: 하나의 객체를 참조값을 통해 여러 변수에서 공유할 수 있음
Address a = new Address("서울");
Address b = a;
b.setValue("부산");
// a,b 둘 다 부산으로 변경
공유 참조와 사이드 이펙트
사이드 이펙트(Side Effect)는 프로그래밍에서 어떤 계산이 주된 작업 외에 추가적인 부수 효과를 일으키는것을 말한다.
이로인해 디버깅이 어려워지고 코드의 안정성이 저하될 수 있다.
(엄한곳에서 버그가 발생해 찾기도 어려운 상태라고 보면 됨)
위 코드에선 사실 Address b만 부산으로 변경하고 싶었는데, 변경되지 말아야할 a도 부산으로 변경되어 버린것이다.
이 문제 해결방법은 단순히 Address b = new Address()처럼 다른 인스턴스를 참조하면 되는데, 애초에 이런 일이 발생하지
않게 자바에서 컴파일 오류를 내주면 되는거 아닌가 생각할 수 있다.
Address a = new Address("서울");
Address b = a; // << 이거
하지만 위와같은 참조값의 공유를 막을 방법이 없다. 자바 문법상에 전혀 문제가 되지 않기 때문이다.
따라서 객체의 공유를 막을 방법이 없다는 것이다..
이 예제는 매우 단순하기 때문에 쉽게 찾을 수 있지만, 복잡한 상황에선 쉽게 찾기 힘들 수 있다.
불변 객체 - 도입
위 사이드 이펙트의 근본적인 원인은 객체를 공유하는 것이 아닌, 공유된 객체의 값을 변경했기 때문이다.
값을 변경하지 못하게 하려면 final을 사용하면 된다.
package lang.immutable.address;
public class ImmutableAddress {
private final String value; // final 설정
public ImmutableAddress(String value) {
this.value = value;
}
public String getValue() {
return value;
}
// public void setValue(String value) {
// this.value = value;
// } set메서드 삭제
@Override
public String toString() {
return "Address{" +
"value='" + value + '\'' +
'}';
}
}
(매우 간단해서 당황)
set메서드만 삭제해도 되지만, 명시적으로 final을 붙임으로써 main메서드에서 다른 개발자가 set메서드를 생성하지 못하게 원천적으로
막아줄 수 있다는 것이다.
package lang.immutable.address;
public class RefMain2 {
public static void main(String[] args) {
ImmutableAddress a = new ImmutableAddress("서울");
ImmutableAddress b = a;
System.out.println(a);
System.out.println(b);
// b.setValue("부산");
b = new ImmutableAddress("부산");
System.out.println("부산 -> b");
System.out.println(a);
System.out.println(b);
}
}
이렇게 되면 다른 사람이 코드를 이어받거나 했을 때 인스턴스를 생성하는 방법으로 하게 될 것이다..
값이 다 같이 바뀌어야 하는 경우엔 위와 같이 객체를 공유해도 되지만, (가족이 다같이 이사를 간다거나?)
그게 아니라면 불변객체로 막아줄 수 있다는 것이다.
불변객체 - 값 변경
불변 객체에서 값을 변경해야하는 경우이다.
package lang.immutable.change;
public class ImmutableObj {
private final int value;
public ImmutableObj(int value) {
this.value = value;
}
public ImmutableObj add(int addValue) {
int result = value + addValue;
return new ImmutableObj(result);
}
public int getValue() {
return value;
}
}
final 이기 때문에 setter를 생성할 수 없다.
따라서 add 메서드에 ImmutableObj 객체를 만들어서 반환해줘야 한다.
package lang.immutable.change;
public class ImmutableMain {
public static void main(String[] args) {
ImmutableObj obj = new ImmutableObj(10);
ImmutableObj obj2 = obj.add(20);
obj.add(20); // 이렇게 사용했을 땐 obj 인스턴스를 참조하기 때문에 불변값 10이 그대로 나오게 됨
// add메서드의 리턴값을 다시 한번 확인해보자
System.out.println(obj.getValue());
System.out.println(obj2.getValue());
}
}
obj2는 add메서드에서 생성한 인스턴스를 참조한다. 따라서 더해진 값 30이 나오게 되는것이다.
불변 객체에서 변경과 관련된 메서드는 보통 새로운 객체를 만들어 반환하기 때문에 꼭 반환값을 받아야 한다.
*불변객체에서 값을 변경하는 경우 withAdd()처럼 with로 시작하는 경우가 많다. set을 사용하는 경우 불변객체가 아니라고 생각할 수 있기때문
'Java' 카테고리의 다른 글
래퍼 클래스, Class 클래스 (3) | 2024.10.17 |
---|---|
String 클래스 (1) | 2024.10.15 |
Object (1) | 2024.10.09 |
제네릭 - 제한된 타입 파라미터/ 와일드카드 (0) | 2024.07.11 |
제네릭(Generic) 메소드 (0) | 2024.07.11 |