[Java의 정석]제6장 객체지향개념 1 - 4. 메서드 오버로딩(Overloading)

류명운

·

2014. 7. 3. 22:29

반응형

4. 메서드 오버로딩(Method Overloading)


4.1 메서드 오버로딩이란?

메서드는 변수와 마찬가지로 같은 클래스 내에서 서로 구별될 수 있어야 하기 때문에 각기 다른 이름을 가져야 한다.
하지만, 자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있도록 했다.
이처럼, 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 메서드 오버로딩(Method Overloading) 또는 간단히 오버로딩(Overloading)이라 한다.
오버로딩(Overloading)의 사전적 의미는 '과적하다.' 즉, 많이 싣는 것을 뜻한다. 보통 하나의 메서드 이름에 하나의 기능만을 구현해야하는데, 하나의 메서드 이름으로 여러 기능을 구현하기 때문에 붙여진 이름이라 생각할 수 있다. 앞으로는 메서드 오버로딩을 간단히 오버로딩이라고 하겠다.



4.2 오버로딩의 조건

같은 이름의 메서드를 정의한다고 해서 무조건 오버로딩인 것은 아니다. 오버로딩이 성립하기 위해서는 다음과 같은 조건을 만족해야한다.


- 메서드 이름이 같아야 한다.
- 매개변수의 개수 또는 타입이 달라야 한다.
- 매개변수는 같고 리턴타입이 다른 경우는 오버로딩이 성립되지 않는다.
(리턴타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.)

비록 메서드의 이름이 같다 하더라도 매개변수가 다르면 서로 구별될 수 있기 때문에 오버로딩이 가능한 것이다. 위의 조건을 만족시키지 못하는 메서드는 중복정의 된 것으로 간주되어 컴파일시에 에러가 발생한다.
[참고]오버로딩된 메서드들은 매개변수에 의해서만 구별될 수 있다.



4.3 오버로딩의 예

오버로딩의 예로 가장 대표적인 것은 println메서드이다. 지금까지 여러분은 println메서드에 괄호 안에 값만 지정해주면 화면에 출력하는데 아무런 어려움이 없었다.
하지만, 실제로는 println메서드를 호출할 때 매개변수로 지정하는 값의 타입에 따라서 호출되는 println메서드가 달라진다.
PrintStream클래스에는 어떤 종류의 매개변수를 지정해도 출력할 수 있도록 아래와 같이 10개의 오버로딩된 println메서드를 정의해놓고 있다.


void println()
void println(boolean x)
void println(char x)
void println(char[] x)
void println(double x)
void println(float x)
void println(int x)
void println(long x)
void println(Object x)
void println(String x)


println메서드를 호출할 때 매개변수로 넘겨주는 값의 타입에 따라서 위의 오버로딩된 메서드들 중의 하나가 선택되어 실행되는 것이다.

오버로딩에 관련된 몇 가지 예를 들어 자세히 살펴보도록 하자.


[보기1]
int add(int a, int b) { return a+b; }
int add(int x, int y) { return x+y; }


위의 두 메서드는 매개변수의 이름만 다를 뿐 매개변수의 타입이 같기 때문에 오버로딩이 성립하지 않는다. 매개변수의 이름이 다르면 메서드 내에서 사용되는 변수의 이름이 달라질 뿐, 아무런 의미가 없다. 그래서, 이 두 메서드는 정확히 같은 것이다. 마치 수학에서, f(x) = x + 1과 f(a) = a + 1이 같은 표현인 것과 같다.
컴파일하면, 'add(int,int) is already defined(이미 같은 메서드가 정의되었다).'라는 메시지가 나타날 것이다.


[보기2]
int add(int a, int b) { return a+b; }
long add(int a, int b) { return (long)(a + b); }


이번 경우는 리턴타입만 다른 경우이다. 매개변수의 타입과 개수가 일치하기 때문에, add(3,3)과 같이 호출하였을 때 어떤 메서드가 호출된 것인지 결정할 수 없기 때문에 오버로딩으로 간주되지 않는다.
이 경우 역시 컴파일하면, 'add(int,int) is already defined(이미 같은 메서드가 정의되었다).'라는 메시지가 나타날 것이다..


[보기3]
long add(int a, long b) { return a+b; }
long add(long a, int b) { return a+b; }


두 메서드 모두 int형과 long형 매개변수가 하나씩 선언되어 있지만, 서로 순서가 다른 경우이다. 이 경우에는 호출 시 매개변수의 값에 의해 호출될 메서드가 구분될 수 있으므로 중복된 메서드 정의가 아닌, 오버로딩으로 간주한다.
이처럼 단지 매개변수의 순서만을 다르게 하여 오버로딩을 구현하면, 사용자가 매개변수의 순서를 외우지 않아도 되는 장점이 있지만, 오히려 단점이 될 수도 있기 때문에 주의해야한다.
예를 들어 add(3,3L)과 같이 호출되면 첫번째 메서드가, add(3L, 3)과 같이 호출되면 두 번째 메서드가 호출된다. 단, 이 경우에는 add(3,3)과 같이 호출할 수 없다. 이와 같이 호출할 경우, 두 메서드 중 어느 메서드가 호출된 것인지 알 수 없기 때문에 메서드를 호출하는 쪽에서 컴파일 에러가 발생한다.


[보기4]
int add(int a, int b) { return a+b; }
long add(long a, long b) { return a+b; }
int add(int[] a) {
for(int i=0,result=0; i < a.length; i++) {
result += a[i];
}
return result;
}


위 메서드들은 모두 바르게 오버로딩되어있다. 정의된 매개변수가 서로 다르긴 해도, 세 메서드 모두 매개변수로 넘겨받은 값을 더해서 그 결과를 돌려주는 일을 한다.
같은 일을 하지만 매개변수를 달리해야하는 경우에, 이와 같이 이름은 같고 매개변수를 다르게 하여 오버로딩을 구현한다.
[참고]세 번째 메서드는 정수형 배열을 매개변수로 넘겨주면, 배열의 모든 원소들의 값을 더해서 결과를 반환하는 작업을 한다.



4.4 오버로딩의 장점

지금까지 오버로딩의 정의와 성립하기 위한 조건을 알아보았다. 그렇다면 오버로딩을 구현함으로써 얻는 이득은 무엇인가에 대해서 생각해보도록 하자.

만일 메서드도 변수처럼 단지 이름만으로 구별된다면, 한 클래스내의 모든 메서드들은 이름이 달라야한다. 그렇다면, 이전에 예로 들었던 10가지의 println메서드들은 각기 다른 이름을 가져야 한다.
예를 들면, 아래와 같은 방식으로 메서드 이름이 변경되어야 할 것이다.


void println()
void printlnBoolean(boolean x)
void printlnChar(char x)
void printlnDouble(double x)
void printlnString(String x)


모두 근본적으로는 같은 기능을 하는 메서드들이지만, 서로 다른 이름을 가져야 하기 때문에 메서드를 작성하는 쪽에서는 이름을 짓기도 어렵고, 메서드를 사용하는 쪽에서는 이름을 일일이 구분해서 기억해야하기 때문에 서로 부담이 된다.
하지만 오버로딩을 통해, 여러 메서드들이 println이라는 하나의 이름으로 정의될 수 있다면, println이라는 이름만 기억하면 되므로, 기억하기도 쉽고, 이름도 짧게 할 수 있어서 오류의 가능성을 많이 줄일 수 있다. 그리고, 메서드의 이름만 보고도 '이 메서드들은 이름이 같으니, 같은 기능을 하겠구나.'라고 쉽게 예측할 수 있게 된다.
또 하나의 장점은 메서드의 이름을 절약할 수 있다는 것이다. 하나의 이름으로 여러 개의 메서드를 정의할 수 있으니, 메서드의 이름을 짓는데 고민을 덜 수 있는 동시에 사용되었어야 할 메서드 이름을 다른 메서드의 이름으로 사용할 수 있기 때문이다.

[예제6-15] OverloadingTest.java

class OverloadingTest {
public static void main(String args[]) {
MyMath2 mm2 = new MyMath2();
System.out.println("mm2.add(3, 3) 결과:" + mm2.add(3,3));
System.out.println("mm2.add(3L, 3) 결과: " + mm2.add(3L,3));
System.out.println("mm2.add(3, 3L) 결과: " + mm2.add(3,3L));
System.out.println("mm2.add(3L, 3L) 결과: " + mm2.add(3L,3L));

int[] a = {100, 200, 300};
System.out.println("mm2.add(a) 결과: " + mm2.add(a));
}
}

class MyMath2 {
int add(int a, int b) {
System.out.print("int add(int a, int b) - ");
return a+b;
}

long add(int a, long b) {
System.out.print("long add(int a, long b) - ");
return a+b;
}

long add(long a, int b) {
System.out.print("long add(long a, int b) - ");
return a+b;
}

long add(long a, long b) {
System.out.print("long add(long a, long b) - ");
return a+b;
}

int add(int[] a) { // 배열의 모든 요소의 합을 결과로 돌려준다.
System.out.print("int add(int[] a) - ");
int result = 0;
for(int i=0; i < a.length;i++) {
result += a[i];
}
return result;
}
} // end of class
[실행결과]
int add(int a, int b) - mm2.add(3, 3) 결과:6
long add(long a, int b) - mm2.add(3L, 3) 결과: 6
long add(int a, long b) - mm2.add(3, 3L) 결과: 6
long add(long a, long b) - mm2.add(3L, 3L) 결과: 6
int add(int[] a) - mm2.add(a) 결과: 600

[참고]add(3L, 3), add(3, 3L), add(3L, 3L)의 결과는 모두 6L이지만, System.out.println(6L);을 수행하면 6이 출력된다.

반응형