Effective Java 52. 다중정의는 신중히 사용하라

Jung Ho Seo·2020년 8월 20일
0

EffectiveJava

목록 보기
14/35
post-thumbnail
post-custom-banner

Overload

용어 정리

  • 재정의(override)
  • 다중정의(overload)

오류가 있는 코드


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 "Unknown Collection";
    }

    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));
    }
}

이 코드에는 오류가 있다 무슨 오류가 있을까??

"Set" 혹은 "List"를 출력할것 같지만, 실제로 수행해보면 "Unkown Collection"만 출력한다
그 이유는 무엇일까? 다중정의(overloading)된 세 classify 중 어느 메서드를 호출할지가 컴파일타임에 결정되기 때문이다

따라서 for문 안의 c는 항상 Collection 타입이다. 런타임에는 타입이 매번 달라지지만, 호출할 메서드를 선태하는 데에는 영향을 주지못한다!!

직관과 어긋나는 이유

이처럼 기대했던것과는 다른 결과가 나오는 이유는 재정의한 메서드는 동적으로 선택되고, 다중정의한 메서드는 정적으로 선택되기 때문이다.

메서드를 재정의 한다면 런타임에 어떤 메서드를 호출할지가 결정된다 (OOP 언어의 다형성)

재정의(override) 메서드 호출 메커니즘


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());
    }
}

이 프로그램은 예상한것처럼 "wine", "sparkling wine", "champagne"을 연속해서 출력한다. for 문의 컴파일 타입인 Wine과는 무관하게 '가장 하위에서 정의한' 메서드로 실행되는 것이다. 그에 비해 다중정의된 메서드 사이에서는 객체의 런타임 타입이 전혀 중요치 않다.

개발자 입장에서 보았을때, 재정의가 작동하는 방식은 정상적으로 보이고, 다중정의는 예외적인 케이스로 보인다. 헷갈릴 수 있는 코드는 작성하지 않는것이 좋다. 그러니 다중정의가 혼란을 이르킬 수 있는 상황은 피하는 것이 좋다

다중정의를 해도 괜찮을때

다중정의로 인한 혼란을 줄이기위한 방법들이 있다.

  • 매개변수 수가 같은 다중정의는 만들지 말자
  • varargs(가변인수)를 사용하는 메서드라면 다중정의를 아예 하지 말아야 한다.

다중정의 말고 메서드 이름을 다르게 하는 길도 있다.

생성자

생성자는 이름을 다르게 지을 수 없으니 두 번째 생성자부터는 무조건 다중정의가 된다. 하지만 이는 정적 팩터리라는 대안을 활용할 수 있는 경우가 많다.(Item 1)

보다 안전하게 다중정의 하기

매개변수 수가 같은 다중정의 메서드가 많더라도, 그중 어느 것이 주어진 매개변수 집합을 처리할지가 명확히 구분된다면, 헷갈릴 일은 없을 것이다. 즉, 매개변수 중 하나 이상이 "근본적으로 다르다(radically different)"면 가능하다.

근본적으로 다르단 말은 두 타입의 값을 어느쪽으로든 형변환이 불가능하단 말이다

ArrayList에는 int를 받는 생성자와 Collection을 받는 생성자가 있는데, 어떤 상황에서든 두 생성자가 헷갈릴 일은 없을것이다.(근본적으로 다름)

오토박싱으로 인한 오류

오토박싱이란?

자바 1.5부터는 오토박싱과 오토 언박싱 이라는 개념이 추가 되었습니다.
먼저 오토박싱의 경우 명시적으로 기본자료형을 래퍼클래스로 감싸주지 않아도 박싱(포장 = wrapping) 해 주는 것입니다.

Integer a = 3; // 내부적으로는 Integer a = new Integer(3); 으로 변환하여 동작
Object o = 3; //마찬가지로 Object o = new Integer(3); 으로 동작하여 다형성 적용

출처: https://dololak.tistory.com/43 [코끼리를 냉장고에 넣는 방법]


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);
        }

        for (int i = 0; i < 3; i++) {
            set.remove(i);
            list.remove(i);
        }
        System.out.println(set + " " + list);
    }
}

이 프로그램은 잘못된 결과를 출력한다. list.remove()는 Object를 받는 경우와 index(int)를 받는 경우 두가지 형태로 다중정의 되어있는데, 여기서 넘긴 i값을 integer로 해석해서 인덱스로 삭제하는 일이 발생한다.

이처럼 제네릭과 오토박싱의 등장으로 이러한 피해를 입은 경우가 있다.

String의 경우

String은 자바 4부터 contentEquals(StringBuffer)라는 메서드를 가지고 있었는데, 자바5에서 StringBuffer, StgringBuilder등 다양한 부류의 타입을 위한 공통 인터페이스 CharSequence가 등장했고, 자연스럽게 String에도 CharSequence 를 받는 conetentEquals가 다중정의되었다.

그 결과 이 장의 지침을 대놓고 어기는 모습이 되었는데, 다행이 이 두메서드 모두 완전히 같은 작업을 수행해주니 해로울것은 없다. 이처럼 기능이 같다면 다중정의를 해도 신경쓸 필요가 없는경우가 있다.

인수를 포워드하여 두 메서드가 동일한 일을 하도록 보장한다.


public boolean contentEquals(StringBuffer sb) {
	return contentEquals((CharSequence) sb);
}

정리

프로그래밍 언어가 다중정의를 허용한다고 해서 다중정의를 꼭 활용하란 뜻은 아니다. 일반적으로 매개변수 수가 같을때는 다중정의를 피하는 게 좋다. 기존 클래스를 수정해 새로운 인터페이스를 구현해야 할 때는 같은 객체를 입력받는 다중정의 메서드들이 모두 동일하게 동작하도록 만들자

profile
책, 글, 개발
post-custom-banner

0개의 댓글