for(Iterator<Element> i = c.iterator(); i.hasNext();){
Element e = i.next();
... // e와 i로 무언가를 한다.
}
...
// 다음 코드는 "i를 찾을 수 없다"는 컴파일 오류를 낸다.
for(Iterator<Element> i2 = c2.iterator(); i.hasNext();){
Element e2 = i2.next();
... // e2와 i2로 무언가를 한다.
}
for (int i=0, n=expensiveComputation(); i<n; i++) {
... // i로 무언가를 한다
}
58. 전통적인 for 문보다는 for-each 문을 사용하라
// 58-1 컬렉션 순회하기 - 더 나은 방법이 있다.
for (Iterator<Element> i = list.iterator(); i.hasNext(); ) {
Element e = i.next();
... // a[i]로 무언가를 한다.
}
// 58-2 배열 순회하기 - 더 나은 방법이 있다.
for (int i = 0; i < list.size(); i++) {
... // a[i]로 무언가를 한다.
}
// 58-3 컬렉션과 배열을 순회하는 올바른 관용구
for (for (Element e : elements) { // ':' 은 in 이라고 읽으면 된다
...
}
// 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);
// 60-1 오류 발생! 금융 계산에 부동소수 타입을 사용했다.
public static void main(String[] args) {
double funds = 1.00;
int itemsBought = 0;
for (double price = 0.10; funds >= price; price += 0.10) {
funds -= price;
itemsBought++;
}
System.out.println(itemsBought + "개 구입");
System.out.println("잔돈(달러):" + funds);
}
// 60-2 BigDecimal을 사용한 해법. 속도가 느리고 쓰기 불편하다.
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. 박싱된 기본 타입보다는 기본 타입을 사용하라
// 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);
}
// 62-2 잘못된 예 - 문자열을 사용해 권한을 부여하였다.
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("클래스를 인스턴스화할 수 없습니다.");
} catch (InvocationTargetException e) {
fatalError("생성자가 예외를 던졌습니다." + e.getCause());
} catch (ClassCastException e ) {
fatalError("Set을 구현하지 않은 클래스입니다.");
}
// 생성한 집합을 사용한다.
s.addAll(Arrays.asList(args).subList(1, args.length));
System.out.println(s);
}
private static void fatalError(String msg){
System.err.println(msg);
System.exit(1);
}