0Pricing
Java Academy · 강의

메서드 오버로딩 규칙

Java가 오버로드된 메서드를 결정하는 방식과 흔히 발생하는 모호성 문제를 이해합니다.

메서드 오버로딩 규칙은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

메서드 오버로딩 규칙

메서드 오버로딩을 사용하면 이름은 같지만 매개변수 목록이 서로 다른 여러 메서드를 정의할 수 있습니다. Java는 정확한 해결 규칙을 사용하여 컴파일 시점에 가장 적합한 일치를 선택합니다.

오버로딩이란?

오버로드된 메서드는 이름은 같지만 매개변수의 개수, 타입 또는 순서가 다릅니다. 반환 타입만으로는 오버로딩할 수 없습니다.

class Calculator {
    int add(int a, int b)     { return a + b; }
    double add(double a, double b) { return a + b; }
    int add(int a, int b, int c)   { return a + b + c; }

    // compile error — same signature, different return type only:
    // long add(int a, int b) { return a + b; }
}

Calculator calc = new Calculator();
System.out.println(calc.add(1, 2));       // 3 (int)
System.out.println(calc.add(1.5, 2.5));   // 4.0 (double)
System.out.println(calc.add(1, 2, 3));    // 6 (3-arg)

해결: 가장 구체적인 일치

Java는 적용 가능한 메서드 중 가장 구체적인 메서드를 선택합니다. 확장 변환, 자동 박싱, 가변 인수를 우선순위에 따라 차례로 적용합니다.

static void print(int x)    { System.out.println("int: "    + x); }
static void print(long x)   { System.out.println("long: "   + x); }
static void print(Object x) { System.out.println("Object: " + x); }

print(42);       // int: 42 (exact match — most specific)
print(42L);      // long: 42 (exact match)
print(3.14);     // Object: 3.14 (no float match, widening then autoboxing)

자동 박싱보다 먼저 적용되는 확장 변환

Java는 기본 타입을 래퍼 타입으로 자동 박싱하는 것보다 기본 타입을 확장 변환하는 것을 우선합니다. 이 때문에 예상하기 어려운 결과가 발생할 수 있습니다.

static void display(long n)    { System.out.println("long: " + n); }
static void display(Integer n) { System.out.println("Integer: " + n); }

int x = 100;
display(x); // long: 100 — widens int to long BEFORE autoboxing to Integer
// This surprises many developers!

static void show(Long n)    { System.out.println("Long"); }
static void show(Integer n) { System.out.println("Integer"); }

// show(100); // compile error — ambiguous! Neither is more specific

null 인수의 모호성

여러 메서드가 참조 타입을 받을 수 있는 경우 오버로드된 메서드에 null을 전달하면 호출이 모호해질 수 있습니다. 모호성을 해결하려면 null을 캐스팅합니다.

static void handle(String s)  { System.out.println("String: " + s); }
static void handle(Integer i) { System.out.println("Integer: " + i); }
static void handle(Object o)  { System.out.println("Object"); }

handle((String) null);  // String: null (cast resolves ambiguity)
handle((Integer) null); // Integer: null
// handle(null);        // compile error: ambiguous (String vs Integer)

상속을 사용한 오버로딩

메서드 해결은 참조 변수의 런타임 타입이 아니라 선언된 타입을 고려합니다. 이는 컴파일 시점에 결정됩니다.

class Animal {}
class Dog extends Animal {}

static void greet(Animal a) { System.out.println("Animal"); }
static void greet(Dog d)    { System.out.println("Dog"); }

Animal a = new Dog(); // declared as Animal
greet(a);   // Animal — compile-time type is Animal!

Dog d = new Dog();
greet(d);   // Dog — compile-time type is Dog

오버로딩과 오버라이딩

오버로딩은 컴파일 시점에 결정되는 정적 디스패치입니다. 오버라이딩은 실제 객체 타입을 기준으로 런타임에 결정되는 동적 디스패치입니다.

class Base {
    void run() { System.out.println("Base.run"); }
}
class Sub extends Base {
    @Override
    void run() { System.out.println("Sub.run"); } // overriding

    void run(int speed) { System.out.println("Sub.run(" + speed + ")"); } // overloading
}

Base b = new Sub();
b.run();     // Sub.run — override (runtime dispatch)
// b.run(10); // compile error — Base doesn't have run(int)

일반적인 오버로딩 패턴

API에서 오버로딩은 전체 매개변수 버전에 위임하는 편의 메서드를 제공할 때 흔히 사용됩니다.

class Connection {
    void connect(String host, int port, int timeout) {
        System.out.println("Connecting to " + host + ":" + port + " timeout=" + timeout);
    }

    // Convenience overloads
    void connect(String host, int port) {
        connect(host, port, 30); // delegate with default timeout
    }

    void connect(String host) {
        connect(host, 5432); // delegate with default port
    }
}

new Connection().connect("db.example.com");
// Connecting to db.example.com:5432 timeout=30

오버로딩과 제네릭

제네릭 메서드 오버로딩에는 주의가 필요합니다. 타입 소거로 인해 겉보기에는 다른 두 시그니처가 컴파일 후에는 동일해질 수 있습니다.

// Type erasure problem: both methods erase to the same signature!
// static void process(List<String> list) {} // compile error
// static void process(List<Integer> list) {} // duplicate after erasure

// Solutions:
// 1. Use different method names: processStrings / processIntegers
// 2. Use a generic method with bounded type:
static <T extends Number> void processNumbers(List<T> list) {
    list.forEach(n -> System.out.println(n.doubleValue()));
}
processNumbers(List.of(1, 2, 3));
processNumbers(List.of(1.5, 2.5));

실전: 빌더 스타일 오버로드

유연한 객체 생성을 제공하기 위해 빌더/팩토리 API에서 오버로딩을 흔히 사용합니다.

class Notification {
    final String title, body;
    final String imageUrl;
    final boolean urgent;

    // Factory overloads for convenience
    static Notification simple(String title, String body) {
        return new Notification(title, body, null, false);
    }
    static Notification urgent(String title, String body) {
        return new Notification(title, body, null, true);
    }
    static Notification withImage(String title, String body, String url) {
        return new Notification(title, body, url, false);
    }
    Notification(String title, String body, String imageUrl, boolean urgent) {
        this.title = title; this.body = body;
        this.imageUrl = imageUrl; this.urgent = urgent;
    }
}

오버로딩의 주의점

다음과 같은 일반적인 오버로딩 실수를 피해야 합니다:

  • 같은 연산에 오버로드를 지나치게 많이 만들기 — 대신 빌더를 사용합니다
  • 매개변수 순서만 다른 오버로드 — 혼란을 일으킵니다
  • 예상하기 어려운 확장 변환: int가 Integer가 아니라 long으로 해석될 수 있습니다
// Confusing: order-based overloads
// void save(String name, int age)  {}
// void save(int age, String name)  {} // very confusing!

// Better: use a record/DTO
record UserData(String name, int age) {}
void save(UserData data) {}

// Surprising widening
static void m(long x)   { System.out.println("long"); }
static void m(Integer x){ System.out.println("Integer"); }
int v = 5;
m(v); // long! widening preferred over autoboxing

빠른 확인

long 매개변수와 Integer 매개변수로 메서드가 오버로드되어 있을 때 int 인수를 전달하면 어느 메서드가 선택됩니까?

복습: 메서드 오버로딩 규칙

핵심 정리:

  • 오버로딩은 매개변수 개수, 형식 또는 순서로 구분되며 — 반환 형식만으로는 구분되지 않습니다(NOT)
  • Java는 인수의 선언된 형식을 사용하여 컴파일 시점에 오버로드를 결정합니다
  • 기본 형식을 래퍼 형식으로 오토박싱하는 것보다 기본 형식을 더 넓은 형식으로 변환하는 것이 우선됩니다
  • 널 인수는 모호성을 일으킬 수 있으므로 형 변환으로 해결합니다: handle((String) null)
  • 오버로딩은 컴파일 시점에, 오버라이딩은 런타임에 이루어지며 서로 다른 메커니즘입니다
  • 전체 매개변수를 받는 기본 메서드에 위임하는 편의 오버로드를 사용합니다

자주 묻는 질문

“메서드 오버로딩 규칙” 강의는 무료인가요?

네 — “메서드 오버로딩 규칙” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“메서드 오버로딩 규칙”에서 뭘 배우나요?

Java가 오버로드된 메서드를 결정하는 방식과 흔히 발생하는 모호성 문제를 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“메서드 오버로딩 규칙” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 메서드 오버로딩 규칙
  2. 가변 인자 매개변수
  3. 정적 필드와 상수
  4. 유틸리티 클래스와 정적 팩터리 메서드
← Java Academy(으)로 돌아가기