오늘은 Immutable class에 대해서 알아보자. Immutable 클래스란 불변의 클래스를 말한다. 불변이라는 말은 변할수 없는 것을 말한다. 불변인 클래스가 좋은점은 객체가 안전하기 때문이다.
java의 대표적인 불변 클래스
java의 대표적인 불변의 클래스는 String이다.
String hi = "hi";
String wonwoo = hi.concat(" wonwoo");
System.out.println(wonwoo);
위의 코드는 변경하는 것으로 보이지만 실제로는 새로운 String을 리턴한다. String의 속성중 value를 변경하지는 않는다. 아래 코드는 String의 concat 메서드이다. 새로운 객체들 다시 만든다.
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
String의 어느 메서드를 봐도 리턴값은 모두 new String() 으로 객체를 다시 만든다. 몇가지 this를 리턴하는게 있는데 그중 하나가 replace() 메서드이다. 그 이유는 replace 할게 없다면 그냥 자기 자신을 리턴한다. 그래도 값은 변경되지 않았으니 불변의 클래스가 된다.
final이면 된다.
불변의 클래스를 만들기 위해서는 몇가지만 알면된다. 클래스의 필드가 final이고 setter가 없으면 된다. 물론 final이면 변경 자체가 안되니 setter가 있어도 소용없다. 아주 간단하게 불변의 클래스를 만들어보자.
public class ProductString {
private final String name;
ProductString(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
위의 코드는 별로 하는 일은 없지만 불변의 클래스이다. setter도 없고 name이라는 필드에도 final 로 선언 되어있다. 만약 여기에 setter가 있어도 컴파일 에러가 나니 모든 변수에 final만 해줘도 불변의 클래스라고 할 수 있다.
String name = "mac book";
ProductString productString = new ProductString(name);
name.concat(" pro");
System.out.println(productString);
결과는 mac book이 출력된다. concat경우에는 새로운 객체를 리턴하니 productString을 출력 할때 기존 name에 있던 변수의 값이 출력 된다.
하지만 우리는 저렇게 단순한 코드를 만들지 않기에 세심하게 봐야 할 것 같다.
final 이어도 불변이 깨질 수 있다.
필드가 모두 final 이더라도 불변이 아닐 수도 있다. 아래의 코드를 유심히 보자.
public class ProductStringBuilder {
private final StringBuilder name;
ProductStringBuilder(StringBuilder name) {
this.name = name;
}
@Override
public String toString() {
return this.name.toString();
}
}
언뜻보면 setter도 없고 필드도 final이니 불변의 클래스 같다. 조금만더 생각해보면 불변이 아니라는 것을 알 수 있다. ProductStringBuilder 클래스의 안에 name이라는 필드는 StringBuilder로 선언 되어있다. StringBuilder는 불변의 클래스가 아니다. StringBuilder의 append 메서드는 문자열을 합치고 자기 자신을 리턴한다. 새로운 객체를 리턴하는 것이 아니고 변경된 값을 리턴한다.
어떻게 하면 불변의 클래스가 아닐 수 있을까?
StringBuilder stringBuilder = new StringBuilder("mac book");
ProductStringBuilder productStringBuilder = new ProductStringBuilder(stringBuilder);
stringBuilder.append(" pro");
System.out.println(productStringBuilder);
위의 String과 똑같은 테스트 코드이다. 하지만 결과는 다르다. 불변의 클래스를 기대하려면 mac book만 출력 되어야 하는데 mac book pro가 출력이 된다. 그럼 우리는 불변의 클래스를 만들 수 없을까? 위의 코드는 간단하게 불변의 클래스로 만들 수 있다.
public class ProductStringBuilder {
private final StringBuilder name;
ProductStringBuilder(StringBuilder name) {
this.name = new StringBuilder(name);
}
@Override
public String toString() {
return this.name.toString();
}
}
위와 같이 다시 새로운 객체를 만들면 된다. 그럼 우리가 원하던 mac book 만 콘솔창에 출력된다.
불변의 클래스를 만들 때에는 위와 같은 상황을 조심해서 만들어야 될 듯하다. 필드가 불변의 클래스가 아닌 경우를..
이상으로 Immutable class에 대해 알아 봤다.