티스토리 뷰

JAVA

오버로딩과 오버라이딩

세댕댕이 2022. 7. 29. 01:00

* 이펙티브 자바 2/E를 읽고 공부하기 위해 기록한 게시글입니다.

41. 오버로딩 할 때는 주의하라

메서드가 오버로딩(Overloading)되면 어떤 메서드가 호출될 지는 컴파일 시점에 정적으로 결정된다.

반면 오버라이딩(Overriding)된 메서드는 런타임 중에 동적으로 결정된다.

 

오버로딩은 메서드 이름은 동일하되 그 이외의 메서드 시그니처(리턴타입이나 파라미터 정보)를 달리 설정하는 것

- 컴파일 시점에 어느 메서드를 실행할 지 선택한다.

- 리턴값만 다른 오버로딩 메서드는 만들 수 없다

public class EffectiveJava {
    public static String classify(LinkedList<?> list) {
        return "LinkedList";
    }

    public static String classify(ArrayList<?> list) {
        return "ArrayList";
    }

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

    public static void main(String[] args) {
        List<?>[] lists = {
                new LinkedList<>(),
                new ArrayList<>(),
                new LinkedList<>()
        };

        for (List<?> list : lists) {
            System.out.println(classify(list));
        }
    }
}

이와같은 코드의 경우, 순차적으로 LinkedList - ArrayList - LinkedList가 출력될 것을 기대하지만 실제 실행해보면 List만 세번 반복해서 나오게 된다.

왜냐하면 오버로딩된 메서드 호출은 컴파일 시점에 결정되기 때문.

-> 컴파일 시점 자료형은 전부 List<?> 로 동일하기 때문. 런타임 시점 자료형은 서로 다르지만 메서드 선택 과정에는 영향을 주지 못한다.

 

 

오버라이딩된 메서드의 선택 기준은 메서드 호출 대상 객체의 자료형이다. 

- 객체 자료형에 따라 실행 도중에 결정된다. 

- 오버라이딩(재정의)된 메서드는 상위 클래스에 선언된 메서드와 같은 시그니처를 같는 하위 클래스 메서드가 재정의된 메서드를 뜻함.

- 하위 클래스에서 재정의한 메서드를 하위 클래스 객체에 대해 호출하면, 해당 객체의 컴파일 시점 자료형과는 관계없이 항상 하위 클래스의 재정의 메서드가 호출된다.

-> 재정의 메서드 가운데 하나를 선택할 때는 객체의 컴파일 시점 자료형은 아무런 영향을 주지 못한다.

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

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

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

public class EffectiveJava {

    public static void main(String[] args) {
        Wine[] wines = {
                new Wine(), new SparklingWine(), new Champagne()
        };

        for (Wine wine : wines) {
            System.out.println(wine.name());
        }
    }
}

이 코드는 wine - sparking wine - 샴페인 순서대로 잘 실행된다.

컴파일 시점 자료형은 모두 Wine으로 동일했지만, 객체의 컴파일 시점 자료형은 아무런 영향을 주지 못했다. 런타임에 실제로 만들어진 객체의 자료형에 의해 호출 메서드가 결정된다.

 

 

오버로딩을 사용할 때는 혼란스럽지 않게 사용할 수 있도록 주의해야 한다.

(오버로딩된 여러 메서드 중 어떤 것이 주어진 인자를 처리할 수 있는 API인지를 명확히 해라)

- varargs(가변인자)를 사용하는 경우라면 가급적 오버로딩을 사용하지 마라

- 같은 수의 인자를 갖는 두 개의 오버로딩 메서드를 만들지 마라 

- 차라리 역할에 따라 메서드 이름을 세분화하는 것이 좋을 수 있다 (writeInt(int), writeLong(long) 등..)

 

* 생성자는 이름이 고정되어있기 때문에 항상 오버로딩된다.

- 생성자는 재정의될 수 없기 때문에 같은 수의 인자를 같는 생성자를 여러개 만들어도 괜찮다.

- 혹시 문제가 있다면 생성자 대신 정적 팩토리 메서드를 활용하면 된다.

 

* 같은 수의 인자를 받는 오버로딩 메서드가 많더라도, 두 개의 오버로딩 메서드를 비교했을 때 그 형식 인자 중 적어도 하나가 "확실히 다른" 경우라면 괜찮다. ("확실히 다른" 경우란,  료형을 서로 형변환할 수 없다는 것을 뜻한다.)

 

 

(결론)

실행될 메서드의 경우 오버로딩은 컴파일 시점에 정적으로, 오버라이딩은 런타임에 동적으로 결정된다.

인자 개수가 같은 오버로딩 메서드를 추가하는 것은 일반적으로 피하는 것이 좋다.

특히 형변환만 추가하면 같은 인자 집합으로 여러 오버로딩 메서드를 호출할 수 있는 상황은 만들지 마라

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함