오버로딩

양성빈·2022년 6월 13일

참고
자바의 정석

오버로딩

오버로딩이란?

메서드도 변수와 마찬기지로 같은 클래스 내에서 서로 구분이 될 수 있어야 함으로 이름을 각기 다른 이름으로 지어야 한다. 하지만, 자바에서 한 클래스 내에 이미 사용된 메서드 이름을 쓰고 싶다면 매개변수의 개수나 타입이 다르면 같은 이름의 메서드를 선언 할 수 있다. 이것을 메서드 오버로딩이라고 한다.

보통은 하나의 메서드에는 하나의 기능을 해야하는게 원칙이지만, 하나의 메서드로 여러 기능을 구현하고 싶은 개발자들을 위해 만들어진게 아닌가 생각해본다.

오버로딩 조건

무조건 이름만 같다고 해서 메서드 오버로딩을 할 수 있는게 아니다. 아래와 같은 조건을 충족해야 오버로딩이라고 할 수 있다.

  1. 메서드 이름이 같아야 한다.
  2. 매개변수의 개수 또는 타입이 달라야 한다.

⚠️ 주의
메서드 오버로딩 구현 조건은 매개변수의 개수나 타입이 달라야한다.
반환타입이 다르더라도 매개변수의 개수나 타입이 같으면 중복정의로 컴파일 에러가 난다.
반환타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.

오버로딩의 예

우리는 이미 오버로딩을 경험했다. 아마 너무 당연시해서 생각을 못하고 쓰고 있을지 모른다. 바로 println() 메서드이다. println()은 안에 매개변수의 개수나 타입에 따라서 호출되는 메서드가 다르다.
println()은 PrintStream 클래스에 10개의 메서드로 오버로딩 되어 있다.

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 메서드의 오버로딩의 예이다. 이것만 보고 많아 이해가 안되었을 수도 있어서 몇가지 예시를 보면서 이해해보자.

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

int add(int x, int y) {
	return x + y;
}

위의 코드를 보면 메서드의 이름은 같지만 매개변수의 타입과 개수가 동일함으로 같은 함수로 인식하여 컴파일 에러를 발생시킨다.

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

long add(int x, int y) {
	return (long) (x + y);
}

위의 코드를 보면 반환타입만 다른 두개의 메서드가 있다. 하자만, 매개변수의 타입이 같고 개수도 같으므로 같은 메서드로 인식이 되어서 컴파일 에러를 발생시킨다.

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

long add(long x, int y) {
	return x + y;
}

위의 코드는 매개변수의 타입이 순서가 다르기만 하고 나머진 같다. 하지만 매개변수의 타입이 다르므로 다른 메서드로 인식이 되어 오버로딩이 성공이 된다. 하지만 위의 코드는 사용자 입장에서는 매개변수의 순서를 외우고 쓰지 않아도 되는 장점이 있지만, 오히려 나중에 단점이 될만한 코드이므로 이런 식으로 오버로딩을 하는 것은 좋지 않은 예이다. 그리고 위의 코드에서 add(3,3)을 호출하게 되면 메서드를 찾을수 없다는 에러메세지가 나올 것이다. 매개변수가 둘다 정수형인 형태는 없기 때문이다.

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

long add(long x, long y) {
	return x + y;
}

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

위의 코드는 모두 바르게 오버로딩이 된 것이다. 정의된 매개변수가 서로 다르긴 해도 3개 메서드 모두 매개변수로 넘겨받은 값을 더해서 그 결과를 돌려주는 일을 한다.
같은 일을 하지만 매개변수를 달리할려면 이와 같이 이름이 같고 매개변수를 다르게 하여 오버로딩을 구해야 한다.

오버로딩 장점

그러면 오버로딩의 장점은 무엇일까? 만일 메서드도 변수처럼 단지 이름만으로 구별된다면 한 클래스내의 모든 메서드가 이름이 달라야 한다. 그러면 같은 기능을 하는 메서드들도 이름이 달라야 함으로 메서드를 작성하는 쪽에서는 이름이 짓기도 어렵고, 메서드를 사용하는 쪽에서 일일이 이름을 기억해야 하기 때문에 서로 부담된다. 예를 든 println 메서드도 마찬가지이다. 매개변수에 따라 이름을 다르게 했다면 이미 자바의 많은 개발자들이 다른 언어로 많이 넘어 갔을 것이다. 하지만 오버로딩으로 인하여 println이라는 이름만 기억해서 사용하면 되고 오류의 가능성도 많이 줄여줄 수 있다. 그리고 이름만 보고도 어떤 기능을 할지 쉽게 유추를 할 수 있을 것이다. 그리고 메서드의 이름을 짓는데 고민을 많이 덜어 줄 수 있다.

가변인자와 오버로딩

우리는 이제까지 메서드의 매개변수가 고정되어 있었으나, JDK1.5부터 동적으로 지정이 가능해졌다. 이 기능을 가변인자라고 한다. 가변인자는 '타입... 변수명' 과 같은 형식으로 선언하면 된다. 대표적인 예로 printf 메서드를 들수 있다.

public PrintStream printf(String format, Object... args) {...}

위와 같이 가변인자 외에도 매개변수가 있다면, 매개변수를 가장 마지막 파라미터로 넣어줘야한다. 만약 아래의 코드처럼 마지막에 두지 않으면 컴파일 에러가 발생한다. 왜냐하면, 마지막 파라미터가 가변인자로 쳐야하는지 아닌지를 모르기 때문이다.

// ERROR
public PrintStream printf(Object... args, String format) {...}

가변인자를 내부적으로 좀 더 살펴보면 가변인자는 내부적으로 배열을 이용하는 것이다. 그래서 가변인자가 선언된 메서드를 호출할 때마다 새로운 배열이 새로 생성된다. 그래서 가변인자를 적어도 빈 매개변수를 넣어도 상관이 없는 것이다. 그러면 의문이 드는게 하나가 있을 것이다. 가변인자가 배열을 이용하는 것이면 애초부터 배열타입으로 선언하는것과 뭐가 다를까? 차이는 바로 가변인자로 매개변수를 지정해두면 빈 파라미터도 허용하지만 배열 타입으로 선언하면 빈 파라미터가 오면 에러가 발생한다.

주의점

가변인자를 사용할때 오버로딩 시 주의점이 있다. 예시를 한번 보자.

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("-", "100", "200");을 호뤂하면 에러가 난다. 왜냐하면 오버로딩한 두 메서드를 구분하지 못하기 때문이다. 가변인자로 한 파라미터인지 2개의 파라미터중 1개만 가변인자로 한것인지 구분이 안되는 것이다. 그래서 가능하면 가변인자를 사용한 메서드는 오버로딩을 하는것이 좋지 않다.

profile
모든 것을 즐길줄 아는 개발자입니다!

0개의 댓글