Copy public class CollectionClassifier {
public static String classify ( Set < ? > s) {
return "집합" ;
}
public static String classify ( List < ? > lst) {
return "리스트" ;
}
public static String classify ( Collection < ? > c) {
return "그 외" ;
}
}
Copy 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 ]) , "그 외" );
}
}
Copy org . opentest4j . AssertionFailedError :
Expected : 그 외
Actual : 집합
org . opentest4j . AssertionFailedError :
Expected : 그 외
Actual : 리스트
다음을 수행 시킨 경우, 실제로 수행해보면 "그 외"만 출력되는 것을 확인할 수 있다. 다중정의(overloading)는 어느 메서드를 호출할지가 컴파일타임에 정해지기 때문 이다. 컴파일 타임에는 항상 Collection<?>
타입이므로, 세번째 메서드인 classify(Collection<?>)
가 호출된 것 이다.
재정의한 메서드(Overriding)는 동적(런타임)으로 선택되며, 다중정의(overloading)한 메서드는 정적(컴파일 타임)으로 선택된다.
Overriding
Copy public class Wine {
String name () { return "포도주" ; }
}
Copy public class SparlklingWine extends Wine {
@ Override
String name () { return "발포성 포도주" ; }
}
Copy public class Champane extends SparlklingWine {
@ Override
String name () { return "샴페인" ; }
}
Copy 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
다시 다중정의로 돌아가, 원하는 대로 값을 얻기 위해서는 다음과 같이 수정해줘야한다.
Copy public class CollectionClassifier {
public static String classify ( Collection < ? > c) {
return c instanceof Set ? "집합" :
c instanceof List ? "리스트" : "그 외" ;
}
}
instanceof
로 명시적 검사로 변경하면, 이전에 실패한 테스트도 통과하는 것을 볼 수 있다.
안전하고, 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말아야한다.
가변인수를 사용하는 메서드는 다중정의를 아예 하지 말아야한다.
다중정의 대안
다중정의하는 대신 메서드 이름을 다르게 지어주는 방법
Copy 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
클래스에서 모든 메서드에 위와 같이 다른 이름을 지어주고 있다.
정적 팩터리 생성자의 경우 두번째 생성자부터는 무조건 다중정의가 된다. 하지만 정적 팩터리 메서드를 사용해 이름을 구분할 수 있다.
그 외
메서드 다중정의 시, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받으면 안된다.
서로 다른 함수형 인터페이스일지라도, 서로 근본적으로 다르지 않다.
다중정의의 메서드가 모두 같은 기능을 한다면, 신경쓰지 않아도 된다.