본문 바로가기

타입 변환과 오버로딩

인수 타입과 자동 형변환이 메서드 선택에 미치는 영향을 이해하고 안전한 회원가입 오버로드를 설계합니다.

같은 의미의 작업이라도 입력 타입이 다를 수 있습니다.

자바는 메서드 이름과 매개변수 목록을 함께 보고 호출 대상을 결정합니다.

같은 이름에 서로 다른 매개변수 목록을 선언하는 기능이 오버로딩입니다.

편리하지만 자동 형변환과 후보 우선순위를 이해하지 못하면 예상과 다른 메서드가 선택되거나 호출 자체가 모호해집니다.


인수 타입과 자동 변환

src/WideningCall.java
public final class WideningCall {
    public static void main(String[] args) {
        int age = 40;
        long total = addBonus(age, 5);
        System.out.println(total);
    }

    private static long addBonus(long age, long bonus) {
        return age + bonus;
    }
}
45

호출 인수는 int 두 개지만 매개변수는 long 두 개입니다.

int 범위의 모든 값을 long이 표현할 수 있으므로 자바가 자동으로 넓혀 전달합니다.

이를 자동 형변환 또는 확대 변환이라고 합니다.

반대 방향은 자동으로 허용되지 않습니다.

private static int normalize(int age) {
    return Math.max(age, 0);
}

long raw = 3_000_000_000L;
int value = normalize(raw); // 컴파일 오류

long 값 일부는 int 범위를 벗어나므로 정보 손실 가능성이 있습니다.

(int) raw처럼 명시적 캐스트를 하면 컴파일되지만, 값이 잘려 전혀 다른 숫자가 될 수 있습니다.

메서드 호출을 통과시키려고 캐스트부터 추가하지 말고 도메인 범위와 매개변수 타입을 먼저 검토합니다.

숫자 확대의 대표 순서는 byte → short → int → long → float → double입니다.

char도 int 이상의 숫자 타입으로 확대될 수 있습니다.

boolean은 숫자와 서로 변환되지 않습니다.

확대라고 해도 큰 long을 float로 바꿀 때 정밀도는 줄 수 있으므로 “컴파일 가능”과 “정보가 완전히 보존됨”을 구분합니다.


오버로딩의 구분 기준

src/OverloadSelection.java
public final class OverloadSelection {
    public static void main(String[] args) {
        printAge(40);
        printAge(40L);
        printAge(40.5);
    }

    private static void printAge(int value) {
        System.out.println("int=" + value);
    }

    private static void printAge(long value) {
        System.out.println("long=" + value);
    }

    private static void printAge(double value) {
        System.out.println("double=" + value);
    }
}
int=40
long=40
double=40.5

세 메서드는 이름이 같지만 매개변수 타입이 다르므로 각각 다른 시그니처입니다.

호출과 정확히 일치하는 타입이 있으면 그 후보가 선택됩니다.

정확한 int 후보가 없다면 printAge(40)에는 long과 double 후보가 모두 적용 가능합니다. 이 고정 인수 예제에서는 long 쪽이 더 구체적이므로 선택됩니다. long 매개변수가 받는 값은 double로 다시 확대해 전달할 수 있지만, 반대 방향인 double에서 long으로는 자동 확대할 수 없습니다. 값 40의 크기로 후보와의 거리를 재는 규칙은 아닙니다.

반환 타입만 바꾸는 것은 오버로딩이 아닙니다.

private static int total(int[] values) { return 0; }
private static long total(int[] values) { return 0L; } // 선언 충돌

호출문 total(values)만 보고 어느 반환값을 원하는지 항상 결정할 수 없기 때문에 매개변수 목록이 같으면 같은 메서드로 취급합니다.

매개변수 이름만 바꾸는 것도 타입과 순서가 같으므로 구분되지 않습니다.


컴파일 시점의 호출 선택

src/StaticArgumentType.java
public final class StaticArgumentType {
    public static void main(String[] args) {
        int exact = 30;
        long widenedBeforeCall = exact;

        label(exact);
        label(widenedBeforeCall);
        label((long) exact);
    }

    private static void label(int value) {
        System.out.println("int overload");
    }

    private static void label(long value) {
        System.out.println("long overload");
    }
}
int overload
long overload
long overload

숫자 값 30 자체가 어느 메서드를 기억하는 것이 아닙니다.

표현식의 컴파일 시점 타입이 후보를 정합니다.

widenedBeforeCall 변수의 타입은 long이고 명시적 (long) 캐스트도 long 표현식을 만들기 때문에 long 오버로드가 선택됩니다.

오버로딩 결정은 실행 중 값의 크기를 보고 바뀌지 않습니다.

long 변수에 30이 들어 있어도 int 범위라는 이유로 int 오버로드를 선택하지 않습니다.

이 규칙 덕분에 컴파일러가 호출 대상을 미리 확정할 수 있습니다.

표현식의 정적 타입, 적용 가능한 오버로드와 실제 선택의 대응

exact와 widenedBeforeCall의 값은 모두 30이지만 표현식 타입이 다르다. 각 행에 명시된 선언 집합 안에서 적용 가능한 후보와 선택 결과를 비교한다.

같은 값이어도 표현식 타입에 따라 후보가 달라집니다

값 30을 int와 long으로 전달한 결과를 비교합니다. 호출 전에 정해진 표현식 타입과 선언된 후보 집합으로 오버로드를 선택합니다.

int exact = 30; · long widenedBeforeCall = exact;

앞 세 행의 선언: label(int) · label(long). 마지막 행은 printAge(long) · printAge(double)만 선언한 별도 예제입니다.
호출 표현식인수의 정적 타입적용 가능한 후보선택
label(exact)int · 값 30label(int)

label(long)

label(int)
label(widenedBeforeCall)long · 값 30label(long)label(long)
label((long) exact)long · 값 30label(long)label(long)
printAge(40)

별도 후보 집합

int · 값 40printAge(long)

printAge(double)

printAge(long)

int 선언 없음

같은 값이어도 표현식 타입에 따라 후보가 달라집니다 int30은int/long가능중int선택;long30변수/캐스트 각각long만;별도int40은long/double중long→double가능관계로long선택 A · int30 B/C · long30 D · 별도 int40 label(exact) 정적 타입 int label(int/long) 선언 widenedBeforeCall 또는 (long)exact 각 정적 타입 long label(int/long) 선언 printAge(40) 정적 타입 int long/double만 선언 label (int) 적용 가능 label (long) 적용 가능 printAge (long) 적용 가능 printAge (double) 적용 가능 label (long) 적용 가능 label(int) 선택 둘 다 적용 가능하지만 int가 더 구체적 label(long) 선택 자동축소 없어 한 후보만 적용 가능 printAge(long) long→double 확대가능 역방향 확대 불가 long이 더 구체적 후보로 가는 선은 적용 가능성 · 선택 후보만 실행, 후보 간 순차 실행 없음
같은 값이어도 표현식 타입에 따라 후보가 달라집니다 int30은int/long가능중int선택;long30변수/캐스트 각각long만;별도int40은long/double중long→double가능관계로long선택 A · label(int/long) 선언 label(exact) int · 값30 실행 전 타입 기준 label (int) 적용 가능 label (long) 적용 가능 label(int) 선택 이 선언 집합에서는 int 후보가 더 구체적 B/C · 별도 long 타입 호출 widenedBeforeCall 또는 (long)exact 각 호출의 타입 long 값은 둘 다30 label(long)만 적용 int로 자동 축소 안 됨 label(long) 선택 D · printAge(long/double) printAge(40) int · 값40 int 선언은 없음 printAge (long) 적용 가능 printAge (double) 적용 가능 printAge(long) long → double 가능 역방향 확대는 안 됨 long 후보가 더 구체적 선택값 크기 때문 아님

앞 세 호출의 선언label(int) · label(long)

  1. label(exact)

    인수는 int, 값은 30입니다. label(int)와 label(long) 모두 적용 가능하고 label(int)를 선택합니다.

  2. label(widenedBeforeCall)

    인수는 long, 값은 30입니다. label(long)만 적용 가능하며 그 후보를 선택합니다.

  3. label((long) exact)

    캐스트한 표현식은 long, 값은 30입니다. label(long)만 적용 가능하며 그 후보를 선택합니다.

  4. printAge(40) · 별도 예제

    선언을 printAge(long), printAge(double) 두 개로 바꾼 예제입니다. int 값 40에 둘 다 확대 적용 가능하며 printAge(long)을 선택합니다. printAge(int)는 선언되어 있지 않습니다.

이 표는 고정 인수의 정확 일치·primitive 확대만 다룹니다. 값의 크기로 실행 중 선택이 바뀌는 규칙이 아니며, 박싱·가변 인수·제네릭은 포함하지 않습니다.


null 인수의 모호성

lab/AmbiguousNullOverload.java
public final class AmbiguousNullOverload {
    public static void main(String[] args) {
        describe(null);
    }

    private static void describe(String value) {
        System.out.println("String");
    }

    private static void describe(StringBuilder value) {
        System.out.println("StringBuilder");
    }
}
컴파일 실패 관찰
error: reference to describe is ambiguous

null은 두 참조 타입 모두에 대입할 수 있고 String과 StringBuilder 사이에는 어느 쪽이 더 구체적이라고 결정할 상속 관계가 없습니다.

컴파일러가 하나를 고를 수 없어 실패합니다.

(String) null처럼 호출 의도를 명시하면 컴파일되지만, API 사용자가 자주 캐스트해야 한다면 오버로드 설계가 지나치게 비슷하다는 신호입니다.

describe(Object)와 describe(String)이 함께 있으면 null 호출은 더 구체적인 String 후보를 선택합니다.

하지만 비슷한 참조 타입 오버로드가 늘수록 새 메서드를 추가했을 때 기존 호출이 모호해질 수 있습니다.

의미가 실제로 다르면 describeText, describeBuilder처럼 이름을 분리하는 편이 안전합니다.

null 후보가 둘이어도 더 구체적인 하나가 있으면 선택된다

두 null 호출 사례에서 후보 적용 가능성을 확인한 뒤 구체성이 없으면 컴파일 실패, Object/String이면 String 선택으로 갈립니다. 컴파일 선택 단계와 메서드 실행을 구분하여 확인할 수 있습니다.

같은 null 입력에 대한 두 후보 집합의 적용 가능성과 구체성 비교

A와 B 모두 null에 적용 가능한 후보는 둘이다. A의 String과 StringBuilder는 서로 하위 타입 관계가 없어 더 구체적인 하나가 없다. B의 String과 Object에서는 String이 Object보다 더 구체적이므로 describe(String)을 선택한다.

null도 더 구체적인 후보 하나가 있어야 선택됩니다

같은 null 입력에 적용 가능한 두 후보를 비교합니다. 상속 관계가 없는 두 타입은 모호하고, String과 Object에서는 String을 선택합니다.

null 오버로드의 첫 차이는 더 구체적인 단일 후보 A는 describe(String)과 describe(StringBuilder), B는 describe(Object)와 describe(String)을 선언하고 같은 describe(null)을 검사한다. 둘 다 이름·개수와 null 적용 가능성을 통과한다. A는 더 구체적인 단일 후보가 없어 컴파일 실패하고 실행하지 않는다. B는 String을 선택하여 String을 출력한다. 없음 있음 · String A · 서로 관계없는 두 참조 타입 describe(String) describe(StringBuilder) B · 상위·하위 타입인 두 후보 describe(Object) describe(String) 1 · 이름·개수 일치 · PASS describe(null) 1 · 이름·개수 일치 · PASS describe(null) 2 · 두 후보 모두 적용 가능 null → String null → StringBuilder 2 · 두 후보 모두 적용 가능 null → Object null → String 첫 차이 · 유일한 더 구체적 후보 3 · 구체성 판정 없음 · FAIL 서로 하위 타입 아님 3 · 구체성 판정 String · PASS String은 Object보다 구체적 컴파일 실패 메서드 실행 안 됨 describe(String) 선택 실행 결과: String
null도 더 구체적인 후보 하나가 있어야 선택됩니다 두독립선언집합모두null적용가능2후보;String/StringBuilder관계없어컴파일모호, Object/String은String선택출력 A · 서로 하위 타입 아님 describe(null) 후보: String StringBuilder 각 선언 집합은 독립 두 후보 모두 적용 null은 둘 다 전달 가능 더 구체적 하나? 없음 서로 하위 타입 아님 컴파일 실패 메서드 실행 안 됨 NullPointer 오류 아님 B · Object/String describe(null) 후보: String Object 각 선언 집합은 독립 두 후보 모두 적용 null은 둘 다 전달 가능 더 구체적 하나? String String이 Object의 하위 describe(String) String 출력 유일한 구체 후보

A · describe(String) / describe(StringBuilder)

  1. 이름·개수 일치 · PASS

    describe(null)에 두 선언 모두 이름과 인수 개수가 맞습니다.

  2. 적용 가능성 · PASS

    null은 String과 StringBuilder 모두에 전달할 수 있어 후보 둘이 남습니다.

  3. 더 구체적인 하나 · FAIL

    String과 StringBuilder는 서로 하위 타입 관계가 아닙니다. 하나를 정할 수 없어 컴파일 실패하며 메서드는 실행하지 않습니다.

B · describe(Object) / describe(String)

  1. 이름·개수 일치 · PASS

    같은 describe(null)에 두 선언 모두 이름과 인수 개수가 맞습니다.

  2. 적용 가능성 · PASS

    null은 Object와 String 모두에 전달할 수 있어 후보 둘이 남습니다.

  3. 더 구체적인 하나 · PASS

    String이 Object보다 구체적입니다. describe(String)을 선택해 String을 출력합니다. 여기가 A와 처음 달라지는 판단입니다.

A에서도 describe((String) null)은 String 후보를 선택합니다. 단, 캐스트가 자주 필요하다면 오버로드의 의도 자체를 검토합니다. 모호한 호출의 컴파일 실패는 NullPointerException이 아닙니다.

이 예의 조건

  • A는 String/StringBuilder, B는 Object/String의 별도 오버로드 집합입니다.
관계도의 보충 설명
원 도표의 설명과 주석
구분내용
전체 읽기 정보null 후보가 둘이어도 더 구체적인 하나가 있으면 선택된다 같은 null 입력에 대한 두 후보 집합의 적용 가능성과 구체성 비교 A와 B 모두 null에 적용 가능한 후보는 둘이다. A의 String과 StringBuilder는 서로 하위 타입 관계가 없어 더 구체적인 하나가 없다. B의 String과 Object에서는 String이 Object보다 더 구체적이므로 describe(String)을 선택한다. null도 더 구체적인 후보 하나가 있어야 선택됩니다 같은 null 입력에 적용 가능한 두 후보를 비교합니다. 상속 관계가 없는 두 타입은 모호하고, String과 Object에서는 String을 선택합니다. A · describe(String) / describe(StringBuilder) 이름·개수 일치 · PASS describe(null) 에 두 선언 모두 이름과 인수 개수가 맞습니다. 적용 가능성 · PASS null 은 String과 StringBuilder 모두에 전달할 수 있어 후보 둘이 남습니다. 더 구체적인 하나 · FAIL String과 StringBuilder는 서로 하위 타입 관계가 아닙니다. 하나를 정할 수 없어 컴파일 실패하며 메서드는 실행하지 않습니다. B · describe(Object) / describe(String) 이름·개수 일치 · PASS 같은 describe(null) 에 두 선언 모두 이름과 인수 개수가 맞습니다. 적용 가능성 · PASS null 은 Object와 String 모두에 전달할 수 있어 후보 둘이 남습니다. 더 구체적인 하나 · PASS String이 Object보다 구체적입니다. describe(String) 을 선택해 String 을 출력합니다. 여기가 A와 처음 달라지는 판단입니다. A에서도 describe((String) null) 은 String 후보를 선택합니다. 단, 캐스트가 자주 필요하다면 오버로드의 의도 자체를 검토합니다. 모호한 호출의 컴파일 실패는 NullPointerException 이 아닙니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
적용 가능한 후보

describe(null)에 이름·개수와 참조 적용 가능성을 검사합니다.

  • A: 두 후보 적용 가능
  • B: 두 후보 적용 가능
  • null이 두 집합 모두에서 단일 후보를 바로 정하지 않습니다.
구체성 판정

A는 서로 하위 타입 아님, B는 String이 더 구체적입니다.

  • A: 유일 후보 없음
  • B: String 선택
  • 상위·하위 관계가 선택의 첫 차이를 만듭니다.
컴파일과 실행

A는 컴파일 실패, B는 선택 메서드를 실행합니다.

  • A: 실행 없음
  • B 출력: String
  • 실패한 후보 판정을 런타임 호출로 해석하지 않습니다.

null 후보가 둘이어도 더 구체적인 하나가 있으면 선택된다

두 null 호출 사례에서 후보 적용 가능성을 확인한 뒤 구체성이 없으면 컴파일 실패, Object/String이면 String 선택으로 갈립니다. 컴파일 선택 단계와 메서드 실행을 구분하여 확인할 수 있습니다.

같은 null 입력에 대한 두 후보 집합의 적용 가능성과 구체성 비교

A와 B 모두 null에 적용 가능한 후보는 둘이다. A의 String과 StringBuilder는 서로 하위 타입 관계가 없어 더 구체적인 하나가 없다. B의 String과 Object에서는 String이 Object보다 더 구체적이므로 describe(String)을 선택한다.

null도 더 구체적인 후보 하나가 있어야 선택됩니다

같은 null 입력에 적용 가능한 두 후보를 비교합니다. 상속 관계가 없는 두 타입은 모호하고, String과 Object에서는 String을 선택합니다.

null 오버로드의 첫 차이는 더 구체적인 단일 후보 A는 describe(String)과 describe(StringBuilder), B는 describe(Object)와 describe(String)을 선언하고 같은 describe(null)을 검사한다. 둘 다 이름·개수와 null 적용 가능성을 통과한다. A는 더 구체적인 단일 후보가 없어 컴파일 실패하고 실행하지 않는다. B는 String을 선택하여 String을 출력한다. 없음 있음 · String A · 서로 관계없는 두 참조 타입 describe(String) describe(StringBuilder) B · 상위·하위 타입인 두 후보 describe(Object) describe(String) 1 · 이름·개수 일치 · PASS describe(null) 1 · 이름·개수 일치 · PASS describe(null) 2 · 두 후보 모두 적용 가능 null → String null → StringBuilder 2 · 두 후보 모두 적용 가능 null → Object null → String 첫 차이 · 유일한 더 구체적 후보 3 · 구체성 판정 없음 · FAIL 서로 하위 타입 아님 3 · 구체성 판정 String · PASS String은 Object보다 구체적 컴파일 실패 메서드 실행 안 됨 describe(String) 선택 실행 결과: String
null도 더 구체적인 후보 하나가 있어야 선택됩니다 두독립선언집합모두null적용가능2후보;String/StringBuilder관계없어컴파일모호, Object/String은String선택출력 A · 서로 하위 타입 아님 describe(null) 후보: String StringBuilder 각 선언 집합은 독립 두 후보 모두 적용 null은 둘 다 전달 가능 더 구체적 하나? 없음 서로 하위 타입 아님 컴파일 실패 메서드 실행 안 됨 NullPointer 오류 아님 B · Object/String describe(null) 후보: String Object 각 선언 집합은 독립 두 후보 모두 적용 null은 둘 다 전달 가능 더 구체적 하나? String String이 Object의 하위 describe(String) String 출력 유일한 구체 후보

A · describe(String) / describe(StringBuilder)

  1. 이름·개수 일치 · PASS

    describe(null)에 두 선언 모두 이름과 인수 개수가 맞습니다.

  2. 적용 가능성 · PASS

    null은 String과 StringBuilder 모두에 전달할 수 있어 후보 둘이 남습니다.

  3. 더 구체적인 하나 · FAIL

    String과 StringBuilder는 서로 하위 타입 관계가 아닙니다. 하나를 정할 수 없어 컴파일 실패하며 메서드는 실행하지 않습니다.

B · describe(Object) / describe(String)

  1. 이름·개수 일치 · PASS

    같은 describe(null)에 두 선언 모두 이름과 인수 개수가 맞습니다.

  2. 적용 가능성 · PASS

    null은 Object와 String 모두에 전달할 수 있어 후보 둘이 남습니다.

  3. 더 구체적인 하나 · PASS

    String이 Object보다 구체적입니다. describe(String)을 선택해 String을 출력합니다. 여기가 A와 처음 달라지는 판단입니다.

A에서도 describe((String) null)은 String 후보를 선택합니다. 단, 캐스트가 자주 필요하다면 오버로드의 의도 자체를 검토합니다. 모호한 호출의 컴파일 실패는 NullPointerException이 아닙니다.

이 예의 조건

  • A는 String/StringBuilder, B는 Object/String의 별도 오버로드 집합입니다.

관계도의 보충 설명

원 도표의 설명과 주석
구분내용
전체 읽기 정보null 후보가 둘이어도 더 구체적인 하나가 있으면 선택된다 같은 null 입력에 대한 두 후보 집합의 적용 가능성과 구체성 비교 A와 B 모두 null에 적용 가능한 후보는 둘이다. A의 String과 StringBuilder는 서로 하위 타입 관계가 없어 더 구체적인 하나가 없다. B의 String과 Object에서는 String이 Object보다 더 구체적이므로 describe(String)을 선택한다. null도 더 구체적인 후보 하나가 있어야 선택됩니다 같은 null 입력에 적용 가능한 두 후보를 비교합니다. 상속 관계가 없는 두 타입은 모호하고, String과 Object에서는 String을 선택합니다. A · describe(String) / describe(StringBuilder) 이름·개수 일치 · PASS describe(null) 에 두 선언 모두 이름과 인수 개수가 맞습니다. 적용 가능성 · PASS null 은 String과 StringBuilder 모두에 전달할 수 있어 후보 둘이 남습니다. 더 구체적인 하나 · FAIL String과 StringBuilder는 서로 하위 타입 관계가 아닙니다. 하나를 정할 수 없어 컴파일 실패하며 메서드는 실행하지 않습니다. B · describe(Object) / describe(String) 이름·개수 일치 · PASS 같은 describe(null) 에 두 선언 모두 이름과 인수 개수가 맞습니다. 적용 가능성 · PASS null 은 Object와 String 모두에 전달할 수 있어 후보 둘이 남습니다. 더 구체적인 하나 · PASS String이 Object보다 구체적입니다. describe(String) 을 선택해 String 을 출력합니다. 여기가 A와 처음 달라지는 판단입니다. A에서도 describe((String) null) 은 String 후보를 선택합니다. 단, 캐스트가 자주 필요하다면 오버로드의 의도 자체를 검토합니다. 모호한 호출의 컴파일 실패는 NullPointerException 이 아닙니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
적용 가능한 후보

describe(null)에 이름·개수와 참조 적용 가능성을 검사합니다.

  • A: 두 후보 적용 가능
  • B: 두 후보 적용 가능
  • null이 두 집합 모두에서 단일 후보를 바로 정하지 않습니다.
구체성 판정

A는 서로 하위 타입 아님, B는 String이 더 구체적입니다.

  • A: 유일 후보 없음
  • B: String 선택
  • 상위·하위 관계가 선택의 첫 차이를 만듭니다.
컴파일과 실행

A는 컴파일 실패, B는 선택 메서드를 실행합니다.

  • A: 실행 없음
  • B 출력: String
  • 실패한 후보 판정을 런타임 호출로 해석하지 않습니다.

출력 메서드 오버로딩

회원 한 명, 회원 목록, 나이 합계를 출력하는 작업을 같은 print 이름으로 표현할 수 있습니다.

매개변수의 모양이 확실히 달라 호출이 모호하지 않습니다.

src/MemberRegistryOverloads.java
public final class MemberRegistryOverloads {
    public static void main(String[] args) {
        String[] emails = {"[email protected]", "[email protected]"};
        int[] age = {40, 50};

        print("[email protected]", 30);
        print(emails, age, 2);
        print(90L);
    }

    private static void print(String email, int age) {
        System.out.println(email + "=" + age);
    }

    private static void print(String[] emails, int[] age, int size) {
        if (size < 0 || size > emails.length || size > age.length) {
            throw new IllegalArgumentException("invalid size");
        }
        for (int index = 0; index < size; index++) {
            print(emails[index], age[index]);
        }
    }

    private static void print(long totalAge) {
        System.out.println("totalAge=" + totalAge);
    }
}

배열 오버로드는 한 회원 오버로드를 반복 호출해 표현 규칙을 재사용합니다.

합계 출력은 long 하나를 받아 전체 나이 합계임을 알 수 있는 이름과 함께 표시합니다.

세 호출은 인수 개수와 타입 모양이 충분히 달라 읽는 사람도 의도를 쉽게 구분합니다.

오버로드는 “비슷해 보이는 모든 작업에 같은 이름을 붙이는 기능”이 아닙니다.

호출자가 같은 개념의 작업이라고 느끼고, 인수 차이만으로 의도가 명확할 때 사용합니다.

저장, 삭제, 요약처럼 행동 자체가 다르면 서로 다른 이름이 더 낫습니다.


오버로드 선택 순서

단순화한 판단 순서는 다음과 같습니다.

  1. 이름과 인수 개수가 맞는 후보를 모읍니다.
  2. 인수 타입이 정확히 일치하는 후보를 우선합니다.
  3. 정확한 후보가 없으면 허용되는 자동 확대 변환 후보를 찾습니다.
  4. 적용 가능한 후보 중 더 구체적인 하나를 고릅니다.
  5. 하나로 정할 수 없으면 모호한 호출로 컴파일 실패합니다.

가변 인수, 박싱, 제네릭까지 들어가면 규칙은 더 많아집니다.

현재 단계에서는 정수 타입을 불필요하게 여러 개 오버로드하거나 관계없는 참조 타입을 나란히 두지 않는 것만으로도 대부분의 모호함을 피할 수 있습니다.


연습 문제

total(int first, int second)와 total(int[] values, int size)를 오버로드하세요.

배열 버전은 size 범위를 검사하고 long 합계를 반환하게 합니다.

main에서 두 버전을 모두 호출합니다.

해설 보기
src/TotalOverloads.java
public final class TotalOverloads {
    public static void main(String[] args) {
        int[] age = {20, 30, 40};

        System.out.println(total(20, 30));
        System.out.println(total(age, 3));
    }

    private static long total(int first, int second) {
        return (long) first + second;
    }

    private static long total(int[] values, int size) {
        if (size < 0 || size > values.length) {
            throw new IllegalArgumentException("invalid size");
        }

        long result = 0L;
        for (int index = 0; index < size; index++) {
            result += values[index];
        }
        return result;
    }
}
50
90

첫 메서드는 더하기 전에 한 피연산자를 long으로 바꿉니다.

int끼리 먼저 더해 오버플로한 뒤 long에 담는 실수를 피합니다.

반환 타입은 두 메서드 모두 long이지만, 오버로드 구분은 매개변수 목록으로 이루어집니다.