// 58-5 같은 버그, 다른 증상!
enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX }
...
Collection<Face> faces = EnumSet.allOf(Face.class);
for (Iterator<Face> i = faces.iterator(); i.hasNext(); )
for (Iterator<Face> j = faces.iterator(); j.hasNext(); )
System.out.println(i.next() + " " + j.next());
// 58-6 문제는 고쳤지만 보기 좋진 않다.
더 나은 방법이 있다!
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) {
Suit suit = i.next();
for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) {
deck.add(new Card(suit, j.next()));
}
}
// 58-7 컬렉션이나 배열의 중첩 반복을 위한 권장 관용구
for (Suit suit : suits)
for (Rank rank : ranks)
deck.add(new Card(suit, rank));
public interface Iterable<E> {
// 이 객체의 원소들을 순회하는 반복자를 반환한다.
Iterator<E> iterator();
}
59. 도서관 학습 및 이용
// 59-1 흔하지만 문제가 심각한 코드!
static Random rnd = new Random();
static int random(int n) {
return Math.abs(rnd.nextInt()) % n;
}
public static void main(String() args) {
int n = 2 * (Integer.MAX_VALUE / 3);
int low = 0;
for (int i = 0; i < 1000000; i++)
if (random(n) < n/2)
low++;
System.out.println(low);
}
// 코드 59-2 transferTo 메서드를 이용해 URL의 내용 가져오기 - 자바 9부터 가능하다.
(353쪽)
public class Curl {
public static void main(String() args) throws IOException {
try (InputStream in = new URL("<https://www.naver.com>").openStream()) {
in.transferTo(System.out);
}
}
}
60. 정확한 답변이 필요한 경우 float 및 double을 피하십시오.
//답을 구하는 어설픈 코드 ..
System.out.println(1.03 - 0.42);
//답을 구하는 어설픈 코드 ..
System.out.println(1.00 - 9 * 0.10);
public static void main(String() args) {
final BigDecimal TEN_DENTS = new BigDecimal(".10");
int itemBought = 0;
BigDecimal funds = new BigDecimal("1.00");
for (BigDecimal price = TEN_DENTS;
funds.compareTo(price) >= 0;
price = price.add(TEN_DENTS)) {
funds = funds.subtract(price);
itemBought++;
}
System.out.println(itemBought + "개 구입");
System.out.println("잔돈(달러) : " + funds);
}
// 60-3 정수 타입을 사용한 해법
public static void main(String() args) {
int itemsBought = 0;
int funds = 100;
for (int price = 10; funds < price; price += 10) {
funds -= price;
itemsBought++;
}
System.out.println(itemsBought + "개 구입");
System.out.println("잔돈(달러):" + funds);
}
61. boxed primitive type보다 primitive type을 선호하라
// 61-1 잘못 구현된 비교자 - 문제를 찾아보자!
Comparator<Integer> naturalOrder =
(i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
// 61-2 문제를 수정한 비교자
Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
int i = iBoxed, j = jBoxed; // 오토박싱
return i < j ? -1 : (i == j ? 0 : 1);
};
// 61-3 기이하게 동작하는 프로그램 - 결과를 맞혀보자!
public class Unbelieable {
static Integer i;
public static void main(String() args) {
if (i == 42)
System.out.println("믿을 수 없군! ");
}
}
// 61-4 끔찍이 느리다! 객체가 만들어지는 위치를 찾았는가? 코드 6-3과 같음
public static void main(String() args){
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
}
public class ThreadLocal {
private ThreadLocal() { } // 객체 생성 불가
// 현 스레드의 값을 키로 구분해 저장한다.
public static void set(String key, Object value);
// (키가 가리키는) 현 스레드의 값을 반환한다.
public static Object get(String key);
}
// 62-3 Key 클래스로 권한을 구분했다.
public class ThreadLocal {
private ThreadLocal() { } // 객체 생성 불가
public static class Key { // (권한)
Key() { }
}
// 위조 불가능한 고유 키를 생성한다.
public static Key getKey() {
return new Key();
}
public static void set(Key key, Object value);
public static Object get(String key);
}
// 62-4 리팩터링하여 Key를 ThreadLocal로 변경
public final class ThreadLocal {
public THreadLocal();
public void set(Object value);
public Object get();
}
// 62-5 매개변수화하여 타입안정성 확보
public final class ThreaddLocal<T> {
public ThreadLocal();
public void set(T value);
public T get();
}
63. 문자열 연결이 느리니 주의하세요.
// 63-1 문자열 연결을 잘못 사용한 예 - 느리다!
public String statement() {
String result = "";
for (int i = 0; i < numItems(); i++)
result += lineForItem(i); // 문자열 연결
return result;
}
// 63-2 StringBuilder를 사용하면 문자열 연결 성능이 크게 개선된다.
public String statement2() {
StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
for (int i = 0; i < numItems(); i++)
b.append(lineForItem(i)); // 문자열 연결 성능이 크게 개선된다.
return b.toString();
}
64. 인터페이스를 사용하여 개체 참조
// 좋은 예. 인터페이스를 타입으로 사용했다.
Set<Son> sonSet = new LinkedHashSet<>();
// 나쁜 예. 클래스를 타입으로 사용했다!
LinkedHashSet<Son> sonSet = new LinkedHashSet<>();
Set<Son> sonSet = new HashSet<>();
// LinkedHashSet()에서 HashSet()으로 쉽게 변환했다.
65. 리플렉션을 통한 인터페이스
// 65-1 리플렉션으로 생성하고 인터페이스로 참조해 활용한다.
public static void main(String() args) {
// 클래스 이름을 Class 객체로 변환
Class<? extends Set<String>> cl = null;
try{
cl = (Class<? extends Set<String>>) // 비검사 형변환!
Class.forName(args(0));
} catch(ClassNotFoundException e) {
fatalError("클래스를 찾을 수 없습니다.
");
}
// 생성자를 얻는다.
Constructor<? extends Set<String>> cons = null;
try{
cons = cl.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
fatalError("매개변수 없는 생성자를 찾을 수 없습니다.
");
}
// 집합의 인스턴스를 만든다.
Set<String> s = null;
try {
s = cons.newInstance();
} catch (IllegalAccessException e){
fatalError("생성자에 접근할 수 없습니다.
");
} catch (InstantiationException e) {
fatalError("클래스를 인스턴스화할 수 없습니다.