한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 '메서드 오버로딩' 또는 간단히 '오버로딩'이라 한다.
자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있다.
오버로딩(overloading)의 사전적 의미는 '과적하다.'로 많이 싣는 것을 뜻한다. 보통 하나의 메서드 이름에 하나의 기능만을 구현해야 하는데, 하나의 메서드 이름으로 여러 기능을 구현하기 때문에 붙여진 이름이라 생각할 수 있다.
1. 메서드 이름이 같아야 한다.
2. 매개변수의 개수 또는 타입이 달라야 한다.
메서드의 이름이 같다 하더라도 매개변수가 다르면 서로 구별될 수 있기 때문에 오버로딩이 가능한 것이다. 위의 조건을 만족시키지 못하는 메서드는 중복 정의로 간주되어 컴파일 시에 에러가 발생한다.
그리고 오버로딩된 메서드들은 매개변수에 의해서만 구별될 수 있으므로 반환타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다는 것에 주의하자.
오버로딩의 예로 가장 대표적인 것은 println메서드이다. println메서드를 호출할 때 매개변수로 지정하는 값의 타입에 따라서 호출되는 println 메서드가 달라진다. PrintStream클래스에는 어떤 종류의 매개변수를 지정해도 출력할 수 있도록 아래와 같이 10개의 오버로딩된 println메서드를 정의해놓고 있다.
void println()
void println(boolean x)
void println(char x)
void println(int x)
void println(long x)
println메서드를 호출할 때 매개변수로 넘겨주는 값의 타입에 따라서 위의 오버로딩된 메서드들 중의 하나가 선택되어 실행되는 것이다.
오버로딩의 몇 가지 예를 살펴보자.
▶ [예시1]
int add(int a, int b) { return a+b; }
int add(int x, int y) { return x+y; }
위의 두 메서드는 매개변수의 이름만 다를 뿐 매개변수의 타입이 같기 때문에 오버로딩이 성립하지 않는다. 이 두 메서드는 같은 것이다. 이 경우 어떤 메서드가 호출된 것인지 결정할 수 없기 때문에 컴파일 에러가 발생한다.
▶ [예시2]
int add(int a, int b) { return a+b; }
long add(int x, int y) { return x+y; }
이번 경우는 리턴타입만 다른 경우이다. 매개변수의 타입과 개수가 일치하기 때문에 이 역시 어떤 메서드가 호출된 것인지 결정할 수 없어 컴파일 에러가 발생한다.
▶ [예시3]
long add(int a, long b) { return a+b; }
long add(long a, int b) { return a+b; }
두 메서드 모두 int형과 long형 매개변수가 하나씩 선언되어 있지만, 서로 순서가 다르므로 메서드가 구분된다. 이 경우 오버로딩으로 간주한다.
▶ [예시4]
int add(int a, int b) { return a+b; }
long add(long a, long b) { return a+b; }
long add(int[] a) { //배열의 모든 요소의 합을 반환한다.
long result = 0;
for(int i=0; i<a.length; i++) {
result += a[i];
}
return result;
}
위 메서드들은 모두 바르게 오버로딩되어 있다. 정의된 매개변수가 서로 다르긴 해도, 세 메서드 모두 매개변수로 넘겨받은 값을 더해서 그 결과를 돌려주는 일을 한다.
같은 일을 하지만 매개변수를 달리해야하는 경우에, 이름은 같고 매개변수를 다르게 하여 오버로딩을 구현한다.
만일 메서드도 변수처럼 단지 이름만으로 구별된다면, 이전에 예로 들었던 10가지의 println메서드들은 각기 다른 이름을 가져야 한다. 예를 들면, 아래와 같은 방식으로 메서드 이름이 변경되어야 할 것이다.
void println()
void printlnBoolean(boolean x)
void printlnChar(char x)
void printlnDouble(double x)
void printlnString(String x)
모두 근본적으로는 같은 기능을 하는 메서드들이지만, 서로 다른 이름을 가져야 하기 때문에 작성하는 쪽도 사용하는 쪽도 불편하다. 오버로딩을 통한다면 위의 여러 메서드들이 println이라는 하나의 이름으로 정의될 수 있다.
가변인자는 [타입... 변수명]과 같은 형식으로 선언한다.
public PrintStream printf(String format, Object... args) {...}
기본에는 메서드의 매개변수 개수가 고정적이었으나 JDK1.5부터 동적으로 지정해 줄 수 있게 되었으며, 이 기능을 '가변인자'라 한다.
위와 같이 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다. 그렇지 않으면, 컴파일 에러가 발생한다. 가변인자인지 아닌지를 구별할 방법이 없기 때문에 허용하지 않는 것이다.
▶ 가변인자 예
만일 여러문자열을 하나로 결합하여 반환하는 concatenate메서드를 작성한다면, 아래와 같이 매개변수의 개수를 다르게 해서 여러 개의 메서드를 작성해야할 것이다.
String concatenate(String s1, String s2) { ... }
String concatenate(String s1, String s2, String s3) { ... }
String concatenate(String s1, String s2, String s3, String s4) { ... }
이럴 때, 가변인자를 사용하면 메서드 하나로 간단히 대체할 수 있다.
String concatenate(String... str) { ... }
이 메서드를 호출할 때는 아래와 같이 인자의 개수를 가변적으로 할 수 있다. 심지어는 인자가 아예 없어도 되고 배열도 인자가 될 수 있다.
System.out.println(concatenate()); // 인자가 없음
System.out.println(concatenate("a")); // 인자가 하나
System.out.println(concatenate("a", "b")); // 인자가 둘
System.out.println(concatenate(new String[]{"A", "B"})); // 배열도 가능
가변인자는 매개변수의 타입을 배열로 하는 것과 어떤 차이가 있을까?
=> 매개변수의 타입을 배열로 하면, 반드시 인자를 지정해 줘야하기 때문에, 위의 코드에서처럼 인자를 생략할 수 없다. 그래서 null이나 길이가 0인 배열을 인자로 지정해줘야 하는 불편함이 있다.
public class VarArgsEx {
public static void main(String[] args) {
String[] strArr = { "100", "200", "300" };
System.out.println(concatenate("", "200", "300"));
System.out.println(concatenate("-", strArr));
System.out.println(concatenate(",", new String[] {"1", "2", "3"}));
System.out.println("["+concatenate(",", new String[0])+"]");
System.out.println("["+concatenate(",")+"]");
}
static String concatenate(String delim, String... args) {
String result = "";
for(String str : args) {
result += str + delim;
}
return result;
}
/*
static String concatenate(String... args){
return concatenate("", args);
}
*/
}
concatenate메서드는 매개변수로 입력된 문자열에 구분자를 사이에 포함시켜 결합해서 반홚나다. 가변인자로 매개변수를 선언했기 때문에 문자열을 개수의 제약없이 매개변수로 지정할 수 있다.
예제1에서 주석처리한 부분은 concatenate메서드의 오버로딩 메서드이다.
주석을 풀면 컴파일에러가 발생한다. 두 오버로딩된 메서드가 구분되지 않아서 발생한 것이다. 가변인자를 선언한 메서드를 오버로딩하면, 메서드를 호출했을 때 이와 같이 구별되지 못하는 경우가 발생하기 쉽기 때문에 가능하면 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋다.
참고
자바의 정석(저자: 남궁성)
오버로딩 부분을 배워가면서 자바 프로그래밍만의 장점을 점점 알아가는 느낌이 들어 재밌었다.
같은 이름으로 오버로딩하며 얻는 장점 부분도 재밌었고, 객체라는 것이 무엇인지에 대해서도 더 깊게 이해가 갔다.
점점 자바가 더 재밌어진다.