본문 바로가기

JAVA

김영한의 실전 자바 중급 1편 (3)

기본형과 참조형의 공유

  • 기본형: 하나의 값을 여러 변수에서 절대로 공유하지 않는다.
  • 참조형: 하나의 객체를 참조값을 통해 여러 변수에서 공유할 수 있다.
package lang.immutable.address;

public class Address {

    private String value;

    public Address(String value) {
        this.value = value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Address{" +
                "value='" + value + '\'' +
                '}';
    }
}

 

package lang.immutable.address;

public class RefMain1_1 {
    public static void main(String[] args) {
        //참조형 변수는 하나의 인스턴스를 공유할 수 있다.
        Address a = new Address("서울");
        Address b = a;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        b.setValue("부산"); //b의 값을 부산으로 변경해야함
        System.out.println("부산 -> b");
        System.out.println("a = " + a); //사이드 이펙트 발생
        System.out.println("b = " + b);
    }
}

 

 

처음에는 a,b 둘다 서울이라는 주소를 가져야 한다고 가정했다.

이후에 b의 주소를 부산으로 변경한다.

실행 결과를 보면 b뿐만 아니라 a의 주소도 함께 부산으로 변경되어 버린다.

 

참조형 변수들은 같은 참조값을 통해 같은 인스턴스를 참조할 수 있다.

결과적으로 참조값을 복사해서 전달하므로 a,b는 같은 x001 인스턴스를 참조한다.

 

공유 참조와 사이드 이펙트

사이드 이펙트는 프로그래밍에서 어떤 계산이 주된 작업 외에 추가적인 부수 효과를 일으키는 것을 말한다.

프로그래밍에서 사이드 이펙트는 보통 부정적인 의미로 사용된다. 프로그램의 특정 부분에서 발생한 변경이 의도치 않게 다른 부분에 영향을 미치는 경우에 발생한다. 이로인해 디버깅이 어려워지고 코드의 안정성이 저하될 수 있다.

 

 

사이드 이펙트 해결 방안

a와 b가 처음부터 서로 다른 인스턴스를 참조하면 된다.

 

package lang.immutable.address;

public class RefMain1_2 {
    public static void main(String[] args) {

        Address a = new Address("서울");
        Address b = new Address("서울");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        b.setValue("부산");
        System.out.println("부산 -> b");
        System.out.println("a = " + a);
        System.out.println("b = " + b);

    }
}

 

 

실행 결과를 보면 b의 주소값만 부산으로 변경된 것을 확인할 수 있다.

 

여러 변수가 하나의 객체를 공유하는 것을 막을 방법은 없다

자바 문법상 Address b = a 와 같은 참조형 변수의 대입은 아무런 문제가 없다.

 

 

불변 객체

위에서 발생한 문제는 공유하면 안되는 객체를 여러 변수에서 공유했기 때문에 발생한 문제이다.

문제의 직접적인 원인은 공유된 객체의 값을 변경한 것에 있다.

 

package lang.immutable;

public class ImmutableAddress {

    private final String value;
    public ImmutableAddress(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
    @Override
    public String toString() {
        return "Address{" +
                "value='" + value + '\'' +
                '}';
    }

}

 

내부 값이 변경되면 안되기 때문에 value의 필드를 final로 선언했다.

값을 변경할 수 있는 setValue()를 제거했다.

이 클래스는 생성자를 통해서만 값을 설정할 수 있고, 이후에는 값을 변경하는 것이 불가능하다.

 

package lang.immutable.address;

import lang.immutable.ImmutableAddress;

public class RefMain2 {

    public static void main(String[] args) {
        ImmutableAddress a = new ImmutableAddress("서울");
        ImmutableAddress b = a; //참조값 대입을 막을 수 있는 방법이 없다.
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        //b.setValue("부산"); //컴파일 오류 발생
        b = new ImmutableAddress("부산");
        System.out.println("부산 -> b");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

}

 

불변이라는 단순한 제약을 사용해서 사이드 이펙트라는 큰 문제를 막을 수 있다.

  • 객체의 공유 참조는 막을 수 없다. 사이드 이펙트가 발생하면 안되는 상황에는 불변 객체를 만들어서 사용하면 된다.
  • 불변 객체는 값을 변경할 수 없다. 불변 객체의 값을 변경하고 싶다면 변경하고 싶은 값으로 새로운 불변 객체를 생성해야 한다.