public class PrimitiveMain {
public static void main(String[] args) {
int a = 10;
int b = a;
System.out.println(a);
System.out.println(b);
b = 20;
System.out.println(a);
System.out.println(b);
}
}
b = a
라고 하면 자바는 항상 값을 복사해서 대입한다.b = 20
이라고 하면 b의 값만 20으로 변경된다.public class ReferenceMain {
public static void main(String[] args) {
Address address1 = new Address("낙원구 행복동");
Address address2 = address1;
System.out.println(address1);
System.out.println(address2);
address2.setAddress("잘 모르구 기쁩니동");
System.out.println(address1);
System.out.println(address2);
}
}
사이드 이펙트(Side Effect)는 프로그래밍에서 어떤 계산이 주된 작업 외에 추가적인 부수 효과를 일으키는 것을 말한다. b의 값을 부산으로 변경한 예시를 다시 분석해보자.
b의 주소값을 변경했으나 a, b는 같은 인스턴스를 참조하기 때문에 a의 값도 같이 변경되어 버린다. 이렇게 주된 작업 외에 추가적인 부수 효과를 일으키는 것을 사이드 이펙트라 한다. 프로그래밍에서 사이드 이펙트는 보통 부정적인 의미로 사용되는데, 사이드 이펙트는 프로그램의 특정 부분에서 발생한 변경이 의도치 않게 다른 부분에 영향을 미치는 경우에 발생한다. 이로 인해 디버깅이 어려워지고 코드의 안정성이 저하될 수 있다.
public class ReferenceMain {
public static void main(String[] args) {
Address address1 = new Address("낙원구 행복동");
Address address2 = address1;
System.out.println(address1);
System.out.println(address2);
change(address2, "잘 모르구 기쁩니동");
System.out.println(address1);
System.out.println(address2);
}
private static void change(Address address, String stringAddress) {
System.out.println("주소 값을 변경합니다. → " + stringAddress);
address.setAddress(stringAddress);
}
}
실행 결과
Address{address='낙원구 행복동'}
Address{address='낙원구 행복동'}
주소 값을 변경합니다. → 잘 모르구 기쁩니동
Address{address='잘 모르구 기쁩니동'}
Address{address='잘 모르구 기쁩니동'}
사이드 이펙트의 더 근본적인 원인을 고려해보면, 객체를 공유하는 것 자체는 문제가 아니다. 객체를 공유한다고 바로 사이드 이펙트가 발생하진 않는다. 문제의 직접적인 원인은 공유된 객체의 값을 변경하는 것에 있다.
객체의 상태(객체 내부의 값, 필드, 멤버 변수)가 변하지 않는 객체를 불변 객체(Immutable Object)라 한다.
public class ImmutableAddress {
private final String value;
public ImmutableAddress(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return "ImmutableAddress{" +
"value='" + value + '\'' +
'}';
}
}
value
를 final
로 선언했다.setValue()
를 제거했다.public class ImmutableAddressMain {
public static void main(String[] args) {
ImmutableAddress immutableAddress1 = new ImmutableAddress("~~구 ~~동");
ImmutableAddress immutableAddress2 = immutableAddress1;
// 공유 참조 & 사이드 이펙트 발생
System.out.println(immutableAddress1);
System.out.println(immutableAddress2);
// immutableAddress1.setValue();
// 불변 객체 사용 & 사이드 이펙트 방지
immutableAddress2 = new ImmutableAddress("XX구 XX동");
System.out.println(immutableAddress1);
System.out.println(immutableAddress2);
}
}
public class Member {
private ImmutableAddress immutableAddress;
private String name;
public Member(ImmutableAddress immutableAddress, String name) {
this.immutableAddress = immutableAddress;
this.name = name;
}
public ImmutableAddress getImmutableAddress() {
return immutableAddress;
}
public void setImmutableAddress(ImmutableAddress immutableAddress) {
this.immutableAddress = immutableAddress;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Member{" +
"immutableAddress=" + immutableAddress +
", name='" + name + '\'' +
'}';
}
}
public class MemberMain {
public static void main(String[] args) {
ImmutableAddress immutableAddress = new ImmutableAddress("XX구 XX동");
Member member1 = new Member(immutableAddress, "아무개");
Member member2 = new Member(immutableAddress, "홍길동");
System.out.println(member1);
System.out.println(member2);
// 홍길동의 주소를 바꿔라.
member2.setImmutableAddress(new ImmutableAddress("낙원구 행복동"));
System.out.println(member1);
System.out.println(member2);
}
}
실행 결과
Member{immutableAddress=ImmutableAddress{value='XX구 XX동'}, name='아무개'}
Member{immutableAddress=ImmutableAddress{value='XX구 XX동'}, name='홍길동'}
Member{immutableAddress=ImmutableAddress{value='XX구 XX동'}, name='아무개'}
Member{immutableAddress=ImmutableAddress{value='낙원구 행복동'}, name='홍길동'}
package me.jangwoojin.address;
public class ImmutableObj {
private final int value;
public ImmutableObj(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public ImmutableObj add(int addValue) {
int result = this.value + addValue;
return new ImmutableObj(result);
}
}
final
키워드를 사용해 생성자를 통해서 한 번만 초기화가 될 수 있다.