final
로 선언한다.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 double realPart() { return re; }
public double imaginaryPart() { return im; }
public Complex plus(Complex c) {
return new Complex(re + c.re, im + c.im);
}
public Complex minus(Complex c) {
return new Complex(re - c.re, im - c.im);
}
public Complex times(Complex c) {
return new Complex(re * c.re - im * c.im,
re * c.im + im * c.re);
}
public Complex dividedBy(Complex c) {
double tmp = c.re * c.re + c.im * c.im;
return new Complex((re * c.re + im * c.im) / tmp,
(im * c.re - re * c.im) / tmp);
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Complex))
return false;
Complex c = (Complex) o;
// == 대신 compare를 사용하는 이유는 63쪽을 확인하라.
return Double.compare(c.re, re) == 0
&& Double.compare(c.im, im) == 0;
}
@Override public int hashCode() {
return 31 * Double.hashCode(re) + Double.hashCode(im);
}
@Override public String toString() {
return "(" + re + " + " + im + "i)";
}
}
📌 final 클래스
📌 re, im 필드
📌 사칙연산 메서드 (plus, minus, times, dividedBy)
함수형 프로그래밍
: 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴.스레드 안전
(thread-safe)하여 동기화할 필요가 없다.실패 원자성
(failure atomicity)를 제공한다. 💡 실패 원자성: 메서드에서 예외가 발생한 후에도 그 객체는 여전히 유효한 상태여야 한다는 성질.
📌 해결 방법 (2가지)
가변 동반 클래스
(companion class)를 둔다.public
으로 제공한다.클래스를 상속하지 못하게 하는 방법은 2가지 있다.
final
클래스로 선언한다. (위의 예제 코드에서 사용한 방법)생성자
를 private
혹은 package-private
으로 만들고, public 정적 팩터리
를 제공한다.두 번째 방법을 사용한 예제 코드는 다음과 같다.
public class Complex {
private final double re;
private final double im;
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
public static Complex valueOf(double re, double im) {
return new Complex(re, im);
}
...
}
📌 핵심 정리
- getter가 있다고 해서 무조건 setter를 만들지는 말자.
- 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
- 단순한 값 객체는 항상 불변으로 만들자.
무거운 값 객체도 불변으로 만들 수 있는지 고심하여야 한다.- 성능 때문에 불변으로 만들 수 없다면, 불변 클래스와 쌍을 이루는
가변 동반 클래스
를public
클래스로 제공하도록 하자.- 불변으로 만들 수 없는 클래스는 변경할 수 있는 부분을 최소한으로 줄이자.
다른 합당한 이유가 없다면 모든 필드는private final
이어야 한다.- 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.
확실한 이유가 없다면 생성자와 정적 팩터리 외에는 그 어떤 초기화 메서드도 public으로 제공해서는 안 된다.