본문 바로가기
My Image
프로그래밍/Java

[Java] Immutable Class (불변 클래스)

by Lim-Ky 2017. 7. 29.
반응형

안녕하세요 이번시간은 Immutable Class(불변 클래스)에 대해서 알아보겠습니다.


Immutable 란?


Immutable을 사전적으로 찾아보면, 불변의, 변경할 수 없는 이라는 뜻임을 알 수 있습니다.

사전적인 의미에서도 알 수 있듯이 Immutable은 변경이 불가합니다. 즉 Immutable Class는 변경이 불가능한 클래스이며, 가변적이지 않는 클래스입니다. 만들어진 Immutable Class는 레퍼런스 타입의 객체이기 때문에 heap영역에 생성됩니다. 


자바에서 이런 Immutable Class로 어떤 것들이 있을까요?

대표적으로 String, Boolean, Integer, Float, Long 등등이 있습니다. 이러한 Immutable Class들은 heap영역에서 변경불가능 한 것이지 재할당을 못하는 것은 아닙니다. 즉 String a = "aa"; 에서 a = "bb" 로 재할당이 가능합니다.  a가 참조하고 있는 heap영역의 객체가 바뀌는 것이지 heap영역에 있는 값이 바뀌는 것이 아닙니다. 

좀 더 풀어말하면, a가 처음에 참조하고 있는 "aa"값이 "bb"로 변경되는 것이 아니라 아예 "bb"라는 새로운 객체를 만들고 그 객체를 a가 참조하게 하는 것입니다. 이렇게 했을 경우 "aa"값을 지니고있는 객체는 이제 그 누구도 참조하고 있지 않는 객체가 되며 gc대상이 됩니다. 



Immutable 의 특징


장점 : 생성자, 접근메소드에 대한 방어 복사가 필요없습니다. 멀티스레드 환경에서 동기화 처리없이 객체를 공유할 수 있습니다.(Thread-safe) 불변이기 때문에 객체가 안전합니다.


단점 : 객체가 가지는 값마다 새로운 객체가 필요합니다. 따라서 메모리 누수와 새로운 객체를 계속 생성해야하기 때문에 성능저하를 발생시킬 수 있습니다.



대표적인 Immutable인 String


자바에서 대표적인 Immutable Class 중 String에 대한 예제를 보면서 알아봅시다.

String 클래스에서 기본적으로 제공해주는 concat 메서드는 문자열을 더해주는 역할을 합니다. 

String a = "Star";
a = a.concat("Craft");
System.out.println(a);

출력결과는 당연히 StarCraft입니다. 여기서 a 라는 String 객체는 처음 Star를 할당 받을 때에 참조된 메모리를 참조하고 있지 않고, concat메서드를 통해 Craft 문자열을 붙인 StarCraft를 참조하는 메모리를 참조합니다.

즉 값을 변화시킨 것이 아니라 String 내부적으로 아예 기존의 Star를 참조하는 객체는 그대로 두고, 새롭게 StarCraft String 객체를 만든 것입니다. 


값을 가지고 있는 객체의 메모리 주소로 보자면, 이렇게 되겠죠??

메모리 

값 

1000 

 "Star"

2000

 "StarCraft"


즉 문자열을 변경하는 작업을 하는 경우 새롭게 객체를 만들어서 리턴합니다. 

concat 메서드 내부를 들여다봅시다.


리턴되는 곳이 new String.. 인 것이 보이시죠? 결국 기존에 있는 String 객체를 변화시키는 것이 아닌 아예 새롭게 객체를 만들어 리턴해줍니다.(위쪽 코드를 보면 return this; 가 있지만, 변화시킬 것이 없는 경우에 해당됩니다. 결국 불변의 규칙을 지키는 것이죠.) String 내부의 모든 메서드들의 return 값은 모두 new Stirng() 입니다.

만약 초당 10000번의 String을 변화시킨다 했을 때 계속 객체를 만들기 때문에 성능저하 문제가 발생할 수 있는 것입니다.



Immutable Class를 만들어보자.


Immutable Class를 만들기 위해서 2가지를 알면 됩니다.

1. 멤버변수를 final로 선언.

2. 접근 메서드를 구현하지 않는다. (Setter메서드)

간단하게 Immutable Class를 만들어 봅시다.


public class ImmutableString {
	
	private final String name;
	
	ImmutableString(String name){
		this.name = name;
	}
	
	@Override
	public String toString(){
		return this.name;
	}

}

name String을 만들고 Amazing을 할당합니다.

다음 이 name객체를 참조하게끔 ImmutableString 생성자에 인자로 주입합니다.

concat 메서드를 통해 AmazingDay라는 새로운 값으로 name에게 재할당합니다.

마지막으로 immutableString을 출력하면, 기존 name을 그대로 참조하고 있기 때문에 Aamzing이 출력됩니다.

즉 concat메서드를 통해 Day 문자열이 합쳐진 AmazingDay는 새로운 객체로 만들어져서 다시 name에게 할당됩니다. 

String name = "Amazing";
ImmutableString immutableString = new ImmutableString(name);
name.concat("Day");
System.out.println(immutableString);

우리가 만든 ImmutableString은 멤버변수가 final로 선언되었기 때문에 수정이 불가하고, 또 setter 접근메서드가 없기때문에 기본적으로 변경시킬 수 있는 모든 상황을 막았습니다. Immutable Class 불변규칙을 지키는 셈이죠.



String vs StringBuilder


잠깐 StringBuilder에 대해서 간단히 짚고넘어갑시다.

StringBuilder 는 String과 달리 mutable(변함,변하기 쉬움)하기 때문에 할당된 값을 변경하더라도, 새로운 객체를 만드는 방식이 아닌 기존 할당된 값을 수정하는 것으로 처리합니다. 즉 문자열 변경과 연산을 하는 경우 기존의 버퍼 크기를 늘리거나 줄이면서 유연하게 동작하는 셈이죠.


좀 전에 직접 Immutable Class를 만든 예제에서 String 타입을 StringBuilder 타입으로 수정해보겠습니다.

public class ImmutableStringBuilder {
	
	private final StringBuilder name;
	
	ImmutableStringBuilder(StringBuilder name){
		this.name = name;
	}
	
	@Override
	public String  toString(){
		return this.name.toString();
	}

}

출력결과 Amazing이 아닌 AmazingDay가 출력됨을 알 수 있습니다.


StringBuilder name = new StringBuilder("Amazing");
ImmutableStringBuilder immutableString = new ImmutableStringBuilder(name);
name.append("Day");
System.out.println(immutableString);

StringBuilder는 불변이 아닌 가변적인 객체이기 때문에 append 메서드를 통해 문자열을 합치고 자기자신을 리턴합니다. StringBuilder의 append 메서드를 들여다 보면 다음과 같이 자기자신(this)리턴합니다.



그럼 인위적으로 만든 Immutable Class 안에서 mutable한 멤버변수 타입일 경우 Immutable Class를 만들 수 없을까? 아닙니다. 방법을 생각해보면 ImmutableStringBuilder 생성자를 조금 수정하면 됩니다.

This.name = new StringBuilder(name); 으로 새로 객체를 생성해 필드값을 초기화해주면, 다시 결과는 Amazing으로 나올 것입니다. 


하지만 알아둘 것은 자바 JSK 1.5버전 이전에는 String과 같은 Immutable한 타입에 많은 연산과 잘못된 사용으로 인한 성능이슈가 많았습니다. 따라서 자바 JDK 1.5 버전 이후엔 컴파일 단계에서 String객체를 선언했어도, StringBuilder로 치환시켜 컴파일 하도록 변경되었습니다. 결국 자바 JDK 1.5 버전 이후에는 String과 StringBuilder의 성능적인 차이가 거의 없어졌다고 볼 수 있습니다. 이것은 참고로 알아둡시다.


이렇게 이번시간은 Immutable이 무엇인지 알아보았습니다.

다음시간은 String, StringBuilder,StringBuffer가 각각 어떤점이 다르고 같은지 알아보도록 하겠습니다.


2017/10/26 - [프로그래밍/Java] - [Java] String vs StringBuffer vs StringBuilder



참고

http://ooz.co.kr/298

http://aoruqjfu.fun25.co.kr/index.php/post/1173

http://hashcode.co.kr/questions/727/







반응형

댓글