1. Introduction
- Method Overloading이란 메서드 이름이 동일한 경우에도 메서드 시그니처가 다르다면 다른 메서드로 인식하는 것을 의미합니다.
- Java 뿐만 아니라 C++, C#과 같이 객체 지향적 언어의 경우 이런 메서드 오버로딩을 언어 자체적으로 지원하고 있습니다.
- 하지만 이것이 메서드 오버로딩을 적극적으로 사용해야 한다는 것은 아닙니다. 이번 챕터에서는 메서드 오버로딩을 사용할 시 발생할 수 있는 문제에 대해 알아보도록 하겠습니다.
2. 메서드 오버로딩의 문제
public class CollectionClassifier {
public static String classify(Set<?> set) {
return "Set";
}
public static String classify(List<?> list) {
return "List";
}
public static String classify(Collection<?> collection) {
return "Other";
}
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));
}
}
}
collections
에는 각각 Set
, List
, Map
이 들어 있습니다. 저희가 기대하는 출력은 다음과 같습니다.
- 먼저 맨 처음의
c
에는 HashSet
이 들어갈 것입니다. 이는 위의 Set<?> set
을 받는 classify
에 해당되므로 출력으로 Set이 출력될 것입니다.
- 두 번째의
c
는 ArrayList
가 들어가고 이는 위의 List<?> list
를 받는 classify
에 해당되므로 List가 출력될 것입니다.
- 마지막은
HashMap
으로 어디에도 해당되지 않지만 Collection<?>
타입에 해당되기 때문에 Other가 출력될 것입니다.
- 문제는 해당 코드를 실행시키면 모두 똑같이 Other가 출력된다는 사실입니다.
이유
- 이유는 오버로딩에 있습니다. 오버로딩된 3개의 classify 중 어떤 classify가 호출될 지 결정되는 시점은 런타임이 아닌 컴파일타임에 이루어집니다.
- 위의 반복문에서
collections
내부에 들어있는 각각의 요소 자체는 런타임 시간에 각각 HashSet
, ArrayList
, HashMap
으로 달라지나, 컴파일 시점에서는 Collection<?>
타입으로 되어 있기 때문에, 이 시점에 오버로딩되어 호출될 메서드는 Collection<?>
타입을 받는 classfiy
로 결정되는 것입니다.
- 이것은 자칫 저희가 생각하는 직관과는 어긋나 보입니다. 이런 이유는 재정의(Overriding)한 메서드는 동적으로 선택되고, 다중정의(Overloading)한 메서드는 정적으로 선택되기 때문입니다.
- 메서드 오버라이딩에 대해 생각해 봅시다. 어떤 메서드를 재정의 했다면 런타임 시점에 해당 객체의 타입에 따라 어떤 메서드가 호출될지가 결정됩니다.
class Parent {
public void print() {
System.out.println("Parent");
}
}
class Child extends Parent {
@Override
public void print() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent p = new Child();
Parent p2 = new Parent();
p2.print();
p.print();
}
}
p2
는 런타임 시점에 Parent
인스턴스 이기 때문에 Parent
의 print
메서드가 실행됩니다.
p
는 런타임 시점에 Child
인스턴스 이기 때문에 Child
의 print
메서드가 실행됩니다.
- 하지만 메서드 다중정의의 경우 런타임 시점의 타입이 어떻게 되는가는 중요하지 않으며 오직 컴파일 시점에서 매개변수의 컴파일타임 타입에 의해 어떤 메서드가 호출될지 결정됩니다.
- 바로 이 부분에서 저희가 생각하는 직관과 실제 상황의 괴리가 발생합니다. 저희가 직관으로 생각한 것과 동일하게 동작하는 것은 메서드 오버라이딩이 됩니다.