불변클래스
인스턴스 내부 값을 수정할 수 없는 클래스.
가변클래스보다 설계와 구현, 사용이 쉽고 오류가 생길 여지가 적어 안전하다.
ex) String, BigDecimal,BigInteger
불변클래스의 규칙.
1.객체의 상태를 변경할 수 있는 set 메서드를 제공하지 않는다.
2.클래스를 확장할 수 없도록 한다. 하위 클래스에서 의도적으로 객체의 상태를 변하는 것을 막아야 한다.
final로 클래스를 선언하거나 생성자를 private으로 두고 복사 생성자를 구현한다.
3.모든 필드를 private로 유지한다. 필드가 참조하고 있는 가변 객체를 클라이언트가
의도적으로 접근해 수정하는 일을 막아야 한다.
접근자를 통해 참조를 반환해서도 안된다. private필드이므로 참조를 변경할 수는 없지만
참조중인 인스턴스 내부 정보가 변경될 수 있다.
public final class Complex {
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
public Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);
}
}
위와같이 연산 메서드가 인스턴스 자신을 수정하는 것이 아닌 새로운 인스턴스를 반환하는
형태를 함수형 프로그래밍 패턴이라 한다.
위와 같은 형태를 사용할 때는 add 와 같은 동사 대신 plus와 같은 전치사형 함수명을
관례적으로 사용한다.
위와 같은 방식이 적용되어 있기에 불변클래스는 failure Atomicity를 보장하며
thread safe 하게 구현할 수 있다.
인스턴스의 상태가 절대 변하지 않고, 메서드 호출 중 실패하더라도
그것이 보장되므로 failure Atomicity를 보장하고
여러 스레드가 동시에 접근해도 기존 객체의 값이 바뀌지 않으므로 thread safe 하다.
불변클래스의 특징을 활용해 자주 쓰일 값들을 미리 상수로 등록해둘 수 있다.
위 코드의 경우에는
public static final Complex ZERO = new Complex(0, 0);
public static final Complex ONE = new Complex(1, 0);
public static final Complex I = new Complex(0, 1);
위와같이 자주 활용될 단순한 복소수를 미리 상수로 만들어둘 수 있다.
불변객체간에는 값을 공유하는것도 가능하다. 불변객체인 BigInteger 클래스 에서는
정수를 비트단위로 나누어 배열에 저장한다.
부호가 반전된 값을 반환하는 negate의 경우에는 새로운 배열을 참조하는 인스턴스를 반환할 수도 있겠지만
어차피 기존 객체의 값을 담당하는 배열 부분이 바뀌지 않을 것이기 때문에
해당 배열의 데이터의 참조를 공유하면서 부호만 바꾼 형태의 인스턴스를 반환하는 형태로 이루어져 있다.
불변클래스의 단점
단점으로는 값이 다르면 항상 새로운 인스턴스를 반환해야 한다는 점에 있다.
BigInteger의 flipBit 메서드를 보면 비트 하나를 토글하는 연산일 뿐인데 정적 팩터리 메서드 valueOf를 사용해
새로운 BigInteger 인스턴스를 반환하고 있다.
백만비트짜리의 BigInteger의 비트를 하나만 전환하려고 해도 거의 같은 클래스를 하나 더 만들어야 한다.
그러한 비효율성을 개선하기 위해서, 불변 객체들에는 package-private으로 선언되어 내부에서 활용할 수 있는
가변 동반 클래스들을 지원하곤 한다.
String과 StringBuilder도 비슷한 예지만, StringBuilder는 독립적으로도 사용할 수 있기에 public으로 설계됐다.
효율
불변객체는 항상 같은 값을 갖고 있기 때문에 계산 비용이 큰 값을 lazy하게 계산하며 캐시해두기도 한다.
private int hashCode; // 자동으로 0으로 초기화된다.
@Override
public int hashCode(){
int result=hashCode;
if(result=0){
result=Short.hashCode(a reaCode);
result=31*result+Short.hashCode(prefix);
result=31*result+Short.hashCode(lineNum);
hashCode=result;
}
return result;
}
BigInteger
BigInteger클래스가 설계될 때는, 아직 개념이 일반적으로 확정되지 않아 불변객체의 역할임에도
public으로 설계되어있다. 그렇기에 메서드를 재정의하여 불변을 해치는 상황이 발생할 수 있다.
이러한 불변객체를 사용할 때에는 신뢰할 수 있는 객체인지 확인하기위해
BigInteger.getclass() == 인스턴스.getclass() 와 같이 검증 결과가 따로 필요하다.
Comparable (0) | 2025.03.12 |
---|---|
객체 참조 해제, Java 참조 유형 (0) | 2025.03.06 |
클래스 빌더 패턴 (0) | 2025.03.04 |
enum 매핑 (0) | 2025.02.28 |
자바 클래스 정렬 기준 설정. PriorityQueue, sort. Comporable, Comporator (코테준비) (0) | 2024.10.20 |