0725
불변객체
불변객체란? 한번 값이 정해지면 바꿀 수 없는 객체
왜 쓸까?
자바의 참조 자료형은 기본적으로 공유자원이다. 이 말은 같은 객체를 여러 변수가 참조 할 수 있다는 것. 이는 개발자가 의도하지 않은 사이드 이팩트를 만들어 내기도 한다. 해결을 위해서는 변수에 값을 복사하지 말고, 새로운 객체를 계속 생성하는 방법이 있다.
불변객체 선언 방법
final 키워드를 사용한다. 객체 내부 인스턴스 변수를 final 키워드로 선언하면, 상수가 되어 초기화 시 한번만 값이 변경 가능하고 그 이후에는 불가능하다. 즉, setter가 필요 없는 것이다.(어차피 못 바꾸므로..) 개발자가 불변객체에 setter를 이용하여 값 변경을 시도 할 때 인텔리제이와 같은 IDE 툴에서는 에러를 일으키고 이것을 보고 불변 객체임을 파악 할 수 도 있다.
불변객체의 값을 바꿔야 할 때는 어쩔까?
기존 객체의 값에 추가로 값을 더하는 add 함수가 있다고 하자. 객체 내부의 인스턴스 변수는 final로 선언되어 변경이 불가능하다. 이럴 경우 add 함수에서는 return 시 new 키워드를 사용하여 새로운 객체를 생성하고 그 내부에(초기화 시) 더해진 값을 넣는다. main 에 반환 된 객체는 이 과정을 거쳐 새롭게 만들어진 객체이다.
메서드 체이닝
package lang.string.chaining;
public class ValueAdder {
private int value;
public ValueAdder add(int addValue) {
value += addValue;
return this;
}
public int getValue() {
return value;
}
}
package lang.string.chaining;
public class MethodChainingMain2 {
public static void main(String[] args) {
ValueAdder adder = new ValueAdder();
ValueAdder adder1 = adder.add(1);
ValueAdder adder2 = adder1.add(2);
ValueAdder adder3 = adder2.add(3);
int result = adder3.getValue();
System.out.println("result = " + result);
System.out.println("result = " + adder);
System.out.println("result = " + adder1);
System.out.println("result = " + adder2);
System.out.println("result = " + adder3);
}
}
/Users/seyoung/Library/Java/JavaVirtualMachines/openjdk-22.0.1/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=51507:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/seyoung/Desktop/study/java/java-mid1/out/production/java-mid1 lang.string.chaining.MethodChainingMain2
result = 6
result = lang.string.chaining.ValueAdder@3f99bd52
result = lang.string.chaining.ValueAdder@3f99bd52
result = lang.string.chaining.ValueAdder@3f99bd52
result = lang.string.chaining.ValueAdder@3f99bd52
Process finished with exit code 0
결국 adder1,2,3 모두 같은 객체를 참조함을 알 수 있다. 그럼 불편하게 이 방식은 왜 쓰는 것일까?
package lang.string.chaining;
public class MethodChainingMain3 {
public static void main(String[] args) {
ValueAdder adder = new ValueAdder();
int result = adder.add(1).add(2).add(3).getValue();
// adder.add(1) 의 return 값을 x001이라고 하자, value: 1
// x001.add(2) -> value: 3
// x001.add(3) -> value: 6
System.out.println("result = " + result);
}
}
메서드 체이닝 기법은 코드를 간결하고 읽기 쉽게 만들어준다.
StringBuilder 와 메서드 체인
StringBuilder의 append() 함수는 자기자신을 반환한다.
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
package lang.string.builder;
public class StringBuilderMain1_2 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
String string = sb.append("A").append("B").append("C").append("D")
.insert(4, "Java")
.delete(4, 8)
.reverse()
.toString();
System.out.println("string = " + string);
}
}
래퍼 클래스
래퍼 클래스는 왜 필요할까?
자바는 객체 지향 언어이다. 즉, 정적 속성과 동적 함수가 하나의 객체에 들어있다. 기본형은 이러한 자바의 특징을 살릴 수 없다.
만일, 기본형 int에 계속 덧셈을 하는 프로그램을 만든다고 해보자. 덧셈을 하는 add() 함수는 int 변수와 관련 있지만 메소드로 내부에 선언 할 수 없다. 또한, 프로그래밍을 하다보면 null 값이 필요한 경우가 있다. 예를 들어 배열에 원소를 찾을 때 값이 없으면 null값을 반환해야 한다. 하지만, 기본형은 null 값을 쓸 수 없으므로 -1과 같은 임의의 숫자를 반환해야만한다. 이때, 값이 없어 -1을 반환한건지 숫자 -1을 반환한 것인지 애매한 경우가 생긴다.
이러한 문제점을 해결하기 위해 나온 것이 바로 wrapper 클래스이다. wrapper 클래스는 기본 자료형을 감싸고 있다고 해서 붙여진 이름이다.
자바가 제공하는 기본 래퍼 클래스는 다음과 같은 특징을 가지고 있다.
1) 불변이다
2) equals 로 비교해야 한다.
컬렉션 프레임워크 - ArrayList