//컬렉션을 집합, 리스트 그외로 구분하기 위한 프로그램(오버로딩)

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

    public static String classify(List<?> lst) {
        return "List";
    }

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

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

        for (Collection<?> c : collections)
            System.out.println(classify(c));
    }
}
  • 결과값은 "그 외"만 3번 출력된다.
  • 다중정의(오버로딩)된 세 classify 중 어느 메소드를 호출할지가 컴파일 타임에 정해지기 때문이다.
  • for문안에서 c는 항상 Collection<?>이기 때문에 세번째 메소드만 출력한다.
  • 재정의한 메소드는 동적으로 선택되고 다중정의한 메소드는 정적으로 선택된다.
    • 재정의 : Overriding
    • 다중정의 : Overloading
//재정의한 메소드

class Wine {
    String name() {
        return "wine";
    }
}

class SparklingWine extends Wine {
    @Override
    String name() {
        return "sparkling wine";
    }
}

class Champagne extends SparklingWine {
    @Override
    String name() {
        return "champagne";
    }
}

public class Overriding {
    public static void main(String[] args) {
        Wine[] wines = {
                new Wine(), new SparklingWine(), new Champagne()
        };
        for (Wine wine : wines)
            System.out.println(wine.name());
    }
}
  • wind, sparklingwine, champagne이 차례대로 출력됨
  • 가장 하위에서 정의한 재정의 메소드가 실행되기 때문
  • 반면 다중정의는 매개변수의 컴파일 타입에 의해 이뤄진다.
  • 위의 classify를 다음과 같이 고치면 된다.
public static String classify(Collection<?> c) {
    return c instanceof Set ? "집합" :
           c instanceof List ? "리스트" : "그 외";
}
  • 공개 API에서 사용자가 매개변수를 넘기면서 어떤 다중정의 메소드가 호추로딜지를 모른다면 프로그램이 오작동하기 쉬워진다.
  • 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말자
  • 가변인수를 사용하는 메소드라면 다중정의를 아에 하면 안된다.(Item 53)

오토박싱 오류

  • 오토박싱은 기본자료형을 래퍼로 감싸주지 않아도 자동으로 래핑되는 것을 의미한다.
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++) {
            //[-3, -2, -1, 0, 1, 2] 기대
            set.add(i); 
            list.add(i);
        }

        for (int i = 0; i < 3; i++) {
            //원소 0, 1, 2를 지우는 것을 기대
            set.remove(i); //[-3, -2, -1] 출력
            list.remove(i); //[-2, 0, 2] 출력
        }
        System.out.println(set + " " + list);
    }
}
  • set.remove(i)의 시그니처는 remove(Object) 라서 원소를 정확히 제거한다.
  • list.remove(i)는 다중정의된 remove(int index)를 선택하기 때문에 인덱스 위치로 제거한다.
    • list.remove( (Integer) i) 로 해주면 인자가 삭제된다.

정리

  • 일반적으로 매개변수 수가 같을 때는 다중정의를 피하라
  • 헷갈릴만한 매개변수는 형변환하여 정확한 다중정의 메소드가 선택되도록 해야 한다.

+ Recent posts