NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

아이템 52. 다중정의는 신중히 사용하라 #52

Closed madplay closed 5 years ago

staywithjoy commented 5 years ago

이 프로그램은 무엇을 출력할까?

public class CollectionClassifier {
    public static String classify(Set<?> s) {
        return "집합";
    }

    public static String classify(List<?> list) {
        return "리스트";
    }

    public static String classify(Collection<?> c) {
        return "그 외";
    }

    public static void main(String[] args) {
        Collection<?>[] collections = {
            new HashSet<String>(),
            new ArrayList<BigInteger>(),
            new HashMap<String, String>().values()
        };

        for (Collection<?> c : collections) {
            System.out.println(classify(c));
        }
    }
}
// 요렇게 출력되지 않을까?
집합
리스트
그 외

재정의(overriding) 메서드 vs 다중정의(overloading) 메서드

class SparkingWine extends Wine { @Override String name() {return "발포성 포도주"; } }

class Champagne extends SparkingWine { @Override String name() {return "샴페인"; } }

public class Overriding { public static void main(String[] args) { List wineList = List.of(new Wine(), new SparkingWine(), new Champagne());

    for (Wine wine : wineList) {
        // for 문에서 wine의 컴파일 타입은 Wine이지만, 하위에서 override한 메서드가 실행됨
        System.out.println(wine.name()); // 예상대로 "포도주", "발포성 포도주", "샴페인" 출력
    }
}

}

- overloading 메서드 : **정적**으로 선택
    - 컴파일 타임에, 오직 매개변수의 **컴파일타임 타입**에 의해 메서드 선택이 이루어집니다.
- ```CollectionClassifier```를 의도대로 사용하려면? 오버로딩 대신 아래처럼 classify를 통합합시다.
```java
public static String classify(Collection<?> c) {
    //return "그 외";
    return c instanceof Set ? "집합" : c instanceof List ? "리스트" : "그 외";
    // return c instanceof Set ? "집합" : (c instanceof List ? "리스트" : "그 외"); 와 같습니다. (컴파일 당시)
}

overloading이 혼란을 일으키지 않도록 하자.

overloading 대신 메서드 이름을 다르게 짓는 건 어떨까?

생성자는 이름을 다르게 못 짓는데 어떡하지?

매개변수가 근본적으로 다르면(radically different) 된다.

하지만 오토박싱이 도입된다면 어떨까!

dragun_autoboxing

새벽_작업의_폐해.png (재미로 보는 원본 및 패러디)

public class SetList {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        List<Integer> list = new ArrayList<>();

        for (int i = -3; i < 3; i++) {
            set.add(i);
            list.add(i);
        }
        System.out.println("add 결과: " + set + " " + list);

        for (int i = 0; i < 3; i++) {
            set.remove(i);
            list.remove(i);
        }
        System.out.println("remove 결과: " + set + " " + list);
    }
}
add 결과: [-3, -2, -1, 0, 1, 2] [-3, -2, -1, 0, 1, 2]
remove 결과: [-3, -2, -1] [-2, 0, 2]

또 람다와 메서드 참조도 도입된다면 어떨까!

public class LambdaOverriding {
    public static void main(String[] args) {
        // 1번. Thread 생성자 호출
        new Thread(System.out::println).start();

        // 2번. ExecutorService의 submit 메서드 호출
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.submit(System.out::println); // 컴파일 오류
    }
}

'근본적으로 다른' 것들

String.java에 있는 contentEquals는???

public static String valueOf(char data[]) { return new String(data); }