Open tonykang22 opened 1 year ago
public static final
다단계 연산
을 제공하거나, 가변 동반 클래스
를 제공하여 대처할 수 있다.다단계 연산
: 여러 연산을 하나로 묶는다.
plus(), minus(), times()
를 사용해야하는 경우, 이 셋을 묶어 연산을 한번에 하면 객체도 하나만 만들어주면 된다.가변 동반 클래스
: 본인의 값을 처리하기 편리하게 해주는 클래스이다.
String
, StringBuilder
// 코드 17-1 불변 복소수 클래스 (106-107쪽)
public final class Complex {
private final double re;
private final double im;
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);
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);
}
// 코드 17-2 정적 팩터리(private 생성자와 함께 사용해야 한다.) (110-111쪽)
public static Complex valueOf(double re, double im) {
return new Complex(re, 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)";
}
}
this.mag
가 변경되면, 추후 다시 negate()
를 호출하면 값이 변경될텐데?
불변 객체
이다.public class BigIntExample {
public static void main(String[] args) {
BigInteger ten = BigInteger.TEN;
BigInteger minusTen = ten.negate();
// Point 가 불변이라서 가능한 케이스이다.
// 이런 이점이 있기에 불변 객체들이 많을 수록 이점이 많아진다.
final Set<Point> points = new HashSet<>();
Point firstPoint = new Point(1, 2);
points.add(firstPoint);
}
}
public class Complex {
private final double re;
private final double im;
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);
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
private static class MyComplex extends Complex {
private MyComplex(double re, double im) {
super(re, im);
}
}
// 코드 17-2 정적 팩터리(private 생성자와 함께 사용해야 한다.) (110-111쪽)
public static Complex valueOf(double re, double im) {
return new MyComplex(re, im);
}
...
}
public class ComplexExample {
// 외부에서 정적 팩터리를 통해 인스턴스를 만들면 MyComplex가 아닌 Complex
public static void main(String[] args) {
Complex complex = Complex.valueOf(1, 0.222);
}
}
public class BigIntegerUtils {
public static BigInteger safeInstance(BigInteger val) {
return val.getClass() == BigInteger.class ? val : new BigInteger(val.toByteArray());
}
}
hashCode()
가 호출될 때 hashCode가 생성되는 lazyLoading
public final class PhoneNumber {
...
// 해시코드를 지연 초기화하는 hashCode 메서드 - 스레드 안정성까지 고려해야 한다. (71쪽)
private volatile int hashCode; // 자동으로 0으로 초기화된다.
@Override public int hashCode() {
if (this.hashCode != 0) {
return hashCode;
}
synchronized (this) {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
this.hashCode = result;
}
return result;
}
}
}
Serializable
과 관련이 있음final을 사용하면 안전하게 초기화 할 수 있다. "안전하다" 란?
public class Whiteship {
private int x;
private int y;
public Whiteship() {
this.x = 1;
this.y = 2;
}
public static void main(String[] args) {
Whiteship whiteship = new Whiteship();
/* 실행 순서는 메모리 모델만 준수한다면 변경될 수 있다
* 단, 한 스레드 내에서 유효한지만 확인한다
* 따라서 멀티스레드 환경에서는 불완전한 초기화가 발생할 수 있다
*
* Object w = new Whiteship()
* whiteship = w
* w.x = 1
* w.y = 2
*
* or
* Object w = new Whiteship()
* w.x = 1
* w.y = 2
* whiteship = w
* */
}
}
병행(concurrency) 프로그래밍에 유용하게 사용할 수 있는 유틸리티 묶음
다른 여러 스레드로 실행하는 여러 오퍼레이션이 마칠 때까지 기다릴 때 사용할 수 있는 유틸리티
public class ConcurrentExample {
public static void main(String[] args) throws InterruptedException {
int N = 10;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
ready(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doneSignal.await(); // wait for all to finish
done();
}
private static void ready() {
System.out.println("준비~~~");
}
private static void done() {
System.out.println("끝!");
}
private static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
} // return;
}
void doWork() {
System.out.println("working thread: " + Thread.currentThread().getName());
}
}
}
아이템 17. 변경 가능성을 최소화 하라.
핵심 정리 1: 불변 클래스
final
로 선언한다. (JLS 17.5)private
으로 선언한다. (아이템 15, 16)예시
예시에 활용될 PhoneNumber 클래스
객체의 상태를 변경하는 메서드를 제외
MyPhoneNumber는 PhoneNumber 타입으로 사용한다면 불변이 아니게 된다.
클래스에 final 키워드로 상속 금지
모든 필드에 final 키워드로 필드 변경을 방지
"자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다."
Person
안에 가변 클래스가 필드로 존재.