본문 바로가기

Java/Java Language

[Java] 오버로딩 (Overloading)

오버로딩의 개념과 예제

하나의 클래스 내에 메서드 이름이 같아도, 매개 변수가 다르면 메서드를 구별할수 있다. 컴파일러(Compiler)는 메서드를 호출할때, 메서드 이름과 매개변수로 분류하기 때문이다. 정확히는 메서드 이름이 동일하여도, 매개변수의 개수 또는 타입이 다르면 메서드를 정의할 수 있다. 따라서 해당 조건이 만족하지 않으면 컴파일 에러를 발생한다. 그리고 반환 타입은 오버로딩을 구현하는데 상관이 없다.

 

클래스의 코드가 로딩될때, 하나의 메서드 이름으로 다른 여러 기능을 하는 메서드들을 같이 로딩할수 있어서 오버로딩(overloading)이라고 한다. 인풋값이 달라지게 되면 다른 기능으로 보는 것이다.

 

다음 코드와 같이, 매개변수 개수 또는 타입이 다른 경우에는 오버로딩이된다. 인자(argument)와 매개변수(parameter)의 일치하거나 자동 타입 캐스팅 되는 범위중에서, 인자와 최적화 되는 매개변수 타입을 받는 메서드를 가장 먼저 호출한다. 그리하여 add(10, 10)으로 호출하게 되면, int add(int a, int b)를 호출한다. 그 뒤로는 int add(int a, long b), int add(long a, long b), int add(short a, short b) 순이다. int 타입이 정수형 중에서 가장 최적화되는 타입이기 때문이다.

int add(short a, short b) {  
    return a + b;  
}

int add(int a, int b) {  
    return a + b;  
} 

// 매개변수의 개수는 같은데 타입이 다른 경우  
int add(int a, long b) {  
    return (int) (a + b);  
}

// 매개변수의 개수는 같은데 타입이 다른 경우  
int add(long a, long b) {  
    return (int) (a + b);  
}

// 매개변수 개수가 다른 경우  
int add(int a, int b, int c) {  
    return a + b + c;  
}  

add(10, 10); // int add(int a, int b), int add(int a, long b, int add(long a, long b), int add(short a, short b) 호출 순
add(10, 10L); // int add(int a, long b) 호출
add(10L, 10L) // int add(long a, long b)호출
add(10, 10, 10); // int add(int a, int b, int c) 호출

 

아래 코드는 매개변수 타입과 개수는 동일하여 오버로딩이 되지 않고, 이미 정의된 메서드이여서 충돌하여 컴파일 에러(method add(int,int) is already defined in class objectoriented.Overloading)가 발생하였다.

int add(int a, int b) {  
    return a + b;  
}  

// 매개변수의 타입과 개수는 동일한데, 변수명이 다른 경우  
int add(int x, int y) {  
    return x + y;  
}

// 매개변수의 타입과 개수는 동일한데, 반환 타입이 다른 경우  
long add(int a, int b) {  
    return (long) (a + b);  
}

오버로딩의 장점

오버로딩의 대표적인 예는, println 메서드이다. println을 호출할때, 어떤 타입의 인자값으로 넘겨도 잘 실행되었다. 그 이유는 각 타입 별로 매개변수를 지정한 메서드를 구현해놨기 때문이다.

 

println 메서드를 호출할 때, 지정된 매개변수의 타입에 맞는 메서드를 호출했었기 때문이다. println이란 동일한 이름의 메서드가 매개변수의 타입에 따라서, 여러 개의 메서드가 정의되어있다는 것이다.

 

실제로 PrintStream 클래스에는 오버로딩된 여러개의 println 메서드가 정의되어있다. 만일 println을 타입별로 메서드 이름을 지정해준다면, 매개변수 타입에 해당하는 메서드명 별로 정의하고 호출해야하기때문에 엄청 귀찮을 것이다.

void printlnFloat(float: x) {...}
void printlnInt(int: x) {...}
void printlnChar(char: x) {...}
...

 

이렇게 오버로딩을 사용하면, 동일한 동작을 하는 메서드를 따로 정의하지 않아도 된다. 그렇다면 메서드를 구현하는 사람도 편하고, 호출하는 사람도 편하다.

// overloading
void println(float: x) {...}
void println(int: x) {...}
void println(char: x) {...}
...

가변인자(varargs)와 오버로딩(overloading)

기존의 메서드는 매개변수의 개수가 고정되어 있엇다. 가변인자(varargs)를 사용하면 동적으로 매개변수를 넘겨줄수 있다. 가변인자는 내부적으로 해당 타입의 배열을 사용한다. 그러나 배열과는 다르게 값이 없어도 호출이 가능하다. 가변인자는 타입 뒤에 ...을 붙이면 된다.

// var argument
int addValues(int... values) { ... }

addValues(); 
addValues(10);
addValues(new int[] {1, 2, 3});

 

원래는 매개변수를 배열로 받을 때, 빈 배열로 넘기게 되면 컴파일 에러가 발생한다. 그러므로 인자를 생략할 수 없었다. 그래서 null이나 길이가 0인 배열을 인자로 지정해줘야한다. 그러나 가변인자는 인자를 생략할수 있다. 따라서 인자 생략이 필요할때만 가변변수를 쓸필요가 있다.

// array argument
int addValues(int values[]) { ... } 

addValues(); // compile error: no arguments
addValues(null); // null
addValues(new int[0]; // 길이가 0인 배열
addValues(10); // run
addValues(new int[] {1, 2, 3}); // run

int addValues(int... values) { ... }
addValues(); // run

 

가변인자는 동적으로 매개변수를 넘긴다는 것은, 해당 타입의 배열을 동적으로 할당 한다는 것이다. 원래는 배열을 매개변수 받으면 주소 값을 복사하였다. 하지만 가변인자는 매개변수로 넘어온 값의 필요한 공간 만큼 메모리에 동적으로 할당하는 작업이 소요된다. 그리하여 인자도 생략가능한 것이다. 따라서 속도가 느리기 때문에 꼭필요하지 않으면 쓰지 않는다. 

 

만일, 가변인자를 써야한다하면 유의할 점 두 가지가 있다. 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다. 그렇지 않으면, 컴파일 에러가 발생한다. 그리고 오버로딩시 조심해야되서, 웬만하면 하지 않는 것이 좋다.

 

아래의 코드르 보면, 가변인자가 다른 매개변수 앞에 있다. printValues(1, 2, 3, 4, 5);의 형태로 메서드 호출시에 어디까지가 가변인자 인지를 알 수 없다.

void printValues(int... values, int value) { ... }
printValues(1, 2, 3, 4, 5);

 

그러나 가변인자를 매개변수 맨 마지막에 배치하면, 이전의 매개변수는 고정이기 때문에 호출시 범위가 명확해진다.

void printValues(int value, int... values) { ... }
printValues(1, 2, 3, 4, 5);

 

물론 가변인자를 두 개 선언하면, 이전과 같이 가변인자 범위가 모호해진다. 그러므로 가변인자는 맨 마지막에 하나만 선언이 가능하다.

// compil error
void printValues(int value, int... values, char... vvalues) { ... }
printValues(1, 2, 3, 4, 5);

 

마지막으로, 오버로딩시 마찬가지로 메서드 호출시에 어디까지가 가변인자 인지를 알 수 없다.

void printValues(int... values) { ... }

// overloading
// compile error -> java: reference to addValues is ambiguous
void printValues(int value, int... values) { ... }
printValues(1, 2, 3, 4, 5);

챰고 자료

  • 자바의 정석 (남궁성 지음)