Menu

자바 메서드 오버로딩: 같은 이름, 다른 매개변수

자바 메서드 오버로딩이 어떻게 여러 메서드가 같은 이름을 공유하면서 다른 매개변수를 받게 하는지, 컴파일러가 어떤 오버로드를 선택하는지, 그리고 피해야 할 모호성 함정을 알아봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

하나의 이름, 여러 버전

이전 페이지에서는 메서드의 매개변수가 그 메서드가 무엇을 받아들이는지 정의하는 방식을 보았습니다. 메서드 오버로딩은 여기서 한 걸음 더 나아갑니다. 매개변수 목록만 다르다면 여러 메서드에 같은 이름을 줄 수 있습니다. 컴파일러는 이들을 서로 다른 메서드로 취급하고, 전달한 인수에 따라 알맞은 것을 고릅니다.

그래서 System.out.printlnint, String, boolean, double을 모두 문제없이 출력합니다. println이 하나만 있는 것이 아니라, 같은 이름을 공유하는 여러 오버로드가 있는 것입니다. 여러분은 의도한 호출을 작성하고, 컴파일러가 그것을 알맞은 버전에 연결합니다.

두 메서드 모두 이름은 square이지만, 하나는 int를, 다른 하나는 double을 받습니다. 리터럴 5int이므로 첫 번째 오버로드가 실행되고, 2.5double이므로 두 번째가 실행됩니다.

무엇이 다른 오버로드로 인정되는가

오버로드는 매개변수 목록에서 달라야 하며, 이는 다음 중 적어도 하나를 의미합니다.

  • 매개변수의 개수가 다름,
  • 매개변수의 타입이 다름, 또는
  • 타입의 순서가 다름.

각 호출의 매개변수 목록은 세 개의 join 메서드 중 정확히 하나와만 일치하므로 혼동이 없습니다.

반환 타입은 인정되지 않는다

초보자가 흔히 빠지는 함정: 반환 타입만으로 오버로딩하려는 시도입니다. 반환 타입은 컴파일러가 사용하는 시그니처의 일부가 아니므로, 다음은 컴파일되지 않습니다.

// 컴파일되지 않음 - 같은 이름, 같은 매개변수, 반환 타입만 다름
static int   value() { return 1; }
static double value() { return 1.0; }   // 오류: value()가 이미 정의됨

value() 같은 호출 지점에서는 인수 어디에도 어느 쪽을 원하는지 단서가 없기 때문에 컴파일러가 둘을 구분할 수 없습니다. 오버로드에 서로 다른 반환 타입을 줄 수는 있지만, 매개변수 목록이 이미 다른 경우에만 가능합니다.

자바가 오버로드를 선택하는 방법

둘 이상의 오버로드가 인수를 받아들일 수 있을 때, 자바는 가장 구체적인 것을 선택하며, 확대 변환보다 정확한 타입 일치를 선호합니다. int 인수로 무슨 일이 일어나는지 살펴봅시다.

show(7)longdouble도 확대 후 7을 담을 수 있음에도 int와 정확히 일치합니다. 정확히 일치하는 오버로드가 제거되어야만 컴파일러가 intlong으로, 그다음 double로 확대합니다. 이 해석은 전적으로 컴파일 시점에, 인수의 선언된 타입을 기준으로 결정됩니다.

모호한 호출에 주의하라

어느 오버로드도 명백히 최선의 일치가 아니면, 컴파일러는 추측을 거부하고 오류를 보고합니다. 이는 모든 참조 타입에 들어맞는 null에서 가장 자주 일어납니다.

static void handle(String s) { }
static void handle(StringBuilder b) { }

handle(null);   // 오류: handle에 대한 참조가 모호함

두 오버로드 모두 null을 받아들이고 어느 쪽도 더 구체적이지 않으므로 이 호출은 컴파일되지 않습니다. 캐스트로 타입을 명시하거나 - handle((String) null) - 오버로드가 충돌하지 않도록 설계를 다시 해서 고칩니다. 오토박싱과 확대를 섞을 때도 같은 주의가 필요합니다. 모든 호출에 명백한 승자가 하나 있도록 오버로드 집합을 충분히 단순하게 유지하세요.

생성자 오버로딩

오버로딩은 일반 메서드에만 국한되지 않습니다. 생성자는 객체를 만드는 여러 방법을 제공하기 위해 이를 끊임없이 사용합니다. 인수가 없는 생성자는 this(...)를 사용해 더 완전한 생성자에 위임할 수 있습니다.

두 생성자는 Point라는 이름을 공유하지만 매개변수 개수에서 다르며, 이는 오버로드된 메서드와 정확히 같습니다. this(...)로 위임하면 초기화 로직이 한곳에 모입니다.

오버로딩 대 오버라이딩

이 둘은 비슷하게 들리지만 서로 관련이 없습니다.

  • 오버로딩 - 같은 이름, 다른 매개변수 목록, 같은 클래스 안. 컴파일러가 컴파일 시점에 버전을 선택합니다. 어떤 연산의 변형을 제공하는 것입니다.
  • 오버라이딩 - 하위 클래스가 상속받은 메서드를 같은 이름 그리고 같은 매개변수로 재정의합니다. 자바는 객체의 실제 타입에 따라 런타임에 버전을 선택합니다. 동작을 대체하는 것입니다(상속다형성에서 만나게 됩니다).

매개변수 목록이 동일하면 오버라이딩하는 것이며(또는 같은 클래스에서 중복 메서드 오류를 일으키며), 다르면 오버로딩하는 것입니다.

다음: Varargs

오버로딩으로 join(a, b)join(a, b, c)를 별개의 메서드로 작성할 수 있습니다. 그러나 개수마다 오버로드를 선언하지 않고 임의의 개수의 인수를 받고 싶다면 어떨까요? 자바의 varargs 구문은 하나의 메서드가 가변 길이 인수 목록을 받을 수 있게 하며, 그것이 다음 페이지의 주제입니다.

자주 묻는 질문

자바에서 메서드 오버로딩이란 무엇인가요?

메서드 오버로딩은 같은 클래스 안에 같은 이름의 메서드를 여러 개 정의하되, 각각 다른 매개변수 목록(매개변수 개수가 다르거나, 타입이 다르거나, 타입의 순서가 다름)을 갖게 하는 것입니다. 컴파일러는 전달한 인수를 각 오버로드의 매개변수와 대조해 어떤 버전을 호출할지 결정합니다. 이것은 런타임이 아니라 컴파일 시점의 결정입니다.

자바에서 두 메서드가 반환 타입만으로 구분될 수 있나요?

아니요. 반환 타입은 오버로딩 관점에서 메서드 시그니처의 일부가 아니므로, 같은 클래스의 int total()double total()은 컴파일 오류가 됩니다. 오버로드는 매개변수 목록, 즉 매개변수의 개수, 타입, 또는 순서에서 달라야 합니다. 반환 타입은 다를 수 있지만, 매개변수의 차이에 추가로 다를 때만 가능하며 그 자체만으로는 안 됩니다.

자바에서 오버로딩과 오버라이딩의 차이는 무엇인가요?

오버로딩은 같은 클래스 안에서 이름은 같지만 매개변수가 다른 여러 메서드로, 컴파일러가 컴파일 시점에 해석합니다. 오버라이딩은 하위 클래스가 상속받은 메서드를 같은 이름과 같은 매개변수로 재정의하는 것으로, 객체의 실제 타입에 따라 런타임에 해석됩니다. 오버로딩은 변형을 제공하는 것이고, 오버라이딩은 동작을 대체하는 것입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기