메서드 오버로딩
같은 개념의 동작이 서로 다른 입력 형태를 받을 때 메서드 오버로딩을 사용할 수 있습니다.
컴파일러는 호출 시점의 인자 개수와 정적 타입을 보고 가장 구체적인 후보를 선택합니다.
반환 타입만 다른 메서드는 호출 문맥만으로 안정적으로 구분할 수 없어 허용되지 않습니다.
자동 형변환과 가변 인자가 함께 있으면 예상하지 못한 후보가 선택되거나 모호해질 수 있습니다.
이 절에서는 편리한 API와 호출 해석의 복잡도 사이 균형을 잡습니다.
매개변수 목록이 다른 메서드를 만든다
개수, 타입, 순서 가운데 하나가 달라야 합니다.
의미가 전혀 다른 동작에 같은 이름을 붙이는 것은 오버로딩이 아니라 혼란을 만듭니다.
컴파일러는 가장 구체적인 후보를 고른다
정확히 일치하는 타입, 확대 변환, 박싱, 가변 인자 순으로 후보 비용이 달라집니다.
null 리터럴은 관련 없는 두 참조 타입 사이에서 모호할 수 있습니다.
호출 인자의 정적 타입을 바꿔 어떤 오버로드가 선택되는지 비교해 봅니다. 오버로딩은 실행 중 실제 객체를 고르는 기능이 아니라 컴파일 시점에 호출 가능한 시그니처 가운데 하나를 결정하는 과정입니다.
public class OverloadResolution {
static String describe(int value) { return "int:" + value; }
static String describe(long value) { return "long:" + value; }
static String describe(String value) { return "text:" + value; }
static String describe(StringBuilder value) { return "builder:" + value; }
public static void main(String[] args) {
System.out.println(describe(3));
System.out.println(describe(3L));
System.out.println(describe("3"));
// describe(null); // 컴파일 오류: String과 StringBuilder 후보가 모호함
}
}3, 3L, "3"을 차례로 넘기면 각각 int, long, String 오버로드가 선택됩니다. 결과는 환경이 아니라 인자의 정적 타입과 변환 규칙으로 결정되므로 어느 컴퓨터에서도 같아야 합니다.
int:3
long:3
text:3리터럴의 정적 타입이 정확히 맞는 오버로드를 먼저 선택합니다.
반환 타입만으로는 오버로딩할 수 없다
호출 결과를 사용하지 않는 문장에서는 어떤 메서드를 부를지 결정할 정보가 없기 때문입니다.
결과 의미가 다르면 이름도 다르게 만드는 편이 낫습니다.
매개변수 목록이 완전히 같으면 반환 타입이 달라도 호출부만으로 어느 메서드를 원하는지 알 수 없습니다. 반환값을 받을 변수 타입으로 후보를 고르지 않으므로 이 선언은 컴파일 단계에서 거부됩니다.
static int area(int side) {
return area(side, side); // 가장 완전한 구현으로 위임
}static int area(int width, int height) {
return width * height;
}// static long area(int side) { return (long) side * side; }
// 컴파일 오류: 매개변수가 같고 반환 타입만 다름기본 구현으로 위임해 중복을 줄인다
짧은 오버로드가 가장 일반적인 오버로드를 호출하도록 만들면 검증과 계산이 한곳에 모입니다.
서로 순환 호출하지 않게 위임 방향을 하나로 정합니다.
여러 오버로드가 같은 검증과 계산을 한다면 가장 일반적인 시그니처 하나로 위임합니다. 기본값을 추가한 호출과 모든 값을 받은 호출의 결과가 같은지 비교하면 위임 과정에서 인자 순서를 잘못 넘긴 오류를 찾을 수 있습니다.
오버로딩은 호출자가 같은 의도로 읽을 수 있는 입력 변형에만 사용하고 모호성이 생기면 별도 이름을 선택합니다.