public class CollectionClassifier {
public static String classify(Set<?> s) {
return "집합";
}
public static String classify(List<?> lst) {
return "리스트";
}
public static String classify(Collection<?> c) {
return "그 외";
}
}
public class OverloadingTest {
@Test
void overloadingTest() {
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
Assertions.assertEquals(CollectionClassifier.classify(collections[0]), "집합");
Assertions.assertEquals(CollectionClassifier.classify(collections[1]), "리스트");
Assertions.assertEquals(CollectionClassifier.classify(collections[2]), "그 외");
}
}
org.opentest4j.AssertionFailedError:
Expected :그 외
Actual :집합
org.opentest4j.AssertionFailedError:
Expected :그 외
Actual :리스트
다음을 수행 시킨 경우, 실제로 수행해보면 "그 외"만 출력되는 것을 확인할 수 있다. 다중정의(overloading)는 어느 메서드를 호출할지가 컴파일타임에 정해지기 때문이다. 컴파일 타임에는 항상 Collection<?> 타입이므로, 세번째 메서드인 classify(Collection<?>)가 호출된 것 이다.
public class Wine {
String name() { return "포도주"; }
}
public class SparlklingWine extends Wine{
@Override
String name() { return "발포성 포도주"; }
}
public class Champane extends SparlklingWine{
@Override
String name() { return "샴페인"; }
}
public class OverridingTest {
@Test
void overridingTest() {
List<Wine> wineList = List.of(new Wine(), new SparlklingWine(), new Champane());
Assertions.assertEquals(wineList.get(0).name(), "포도주");
Assertions.assertEquals(wineList.get(1).name(), "발포성 포도주");
Assertions.assertEquals(wineList.get(2).name(), "샴페인");
}
}
name() 메서드는 하위클래스에서 각각 재정의하고 있다. 테스트 코드 수행시, 예상한 대로 "포도주", "발포성 포도주", "샴페인"이 출력되는 것을 확인할 수 있다. 메서드 재정의는 컴파일 타임에 그 인스턴스의 타입이 무엇인지는 상관 없으며, 가장 하위에서 재정의한 메서드가 실행되는 것이다.
Overloading
다시 다중정의로 돌아가, 원하는 대로 값을 얻기 위해서는 다음과 같이 수정해줘야한다.
public class CollectionClassifier {
public static String classify(Collection<?> c) {
return c instanceof Set ? "집합" :
c instanceof List ? "리스트" : "그 외";
}
}
instanceof로 명시적 검사로 변경하면, 이전에 실패한 테스트도 통과하는 것을 볼 수 있다.
다중정의가 혼란을 주는 상황은 피해야한다.
안전하고, 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말아야한다.
가변인수를 사용하는 메서드는 다중정의를 아예 하지 말아야한다.
다중정의 대안
다중정의하는 대신 메서드 이름을 다르게 지어주는 방법
public void writeBoolean(boolean val) throws IOException {
bout.writeBoolean(val);
}
public void writeByte(int val) throws IOException {
bout.writeByte(val);
}
public void writeShort(int val) throws IOException {
bout.writeShort(val);
}
public void writeChar(int val) throws IOException {
bout.writeChar(val);
}
ObjectOutputStream 클래스에서 모든 메서드에 위와 같이 다른 이름을 지어주고 있다.
정적 팩터리 생성자의 경우 두번째 생성자부터는 무조건 다중정의가 된다. 하지만 정적 팩터리 메서드를 사용해 이름을 구분할 수 있다.