[자바/Java] 오버로딩(Overloading)

kindof

·

2021. 8. 2. 08:59

🏃 0.  들어가면서

자바의 가장 큰 무기 중 하나는 다형성(polymorphism)입니다.

 

다형성을 풀어서 설명해보면 하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작할 수 있는 것을 의미한다고 볼 수 있습니다.

 

이번 글에서는 '다형성을 어떻게 확보할 수 있는가'에 대한 방법으로 오버로딩(Overloading)에 대한 공부를 해보겠습니다. 

 

📃 1. 오버로딩(Overloading)이란? 

If a class has multiple methods having same name but different in parameters, it is known as Method Overloading

하나의 클래스 안에 같은 이름을 갖지만 다른 파라미터를 가지는 여러 개의 메서드가 존재하면, 이를 메서드 오버로딩(Method Overloading)이라고 합니다.

 

오버로딩은 위에서 정의한 것처럼 하나의 메서드로 여러 기능을 구현하기 때문에 붙여진 이름입니다.

 

‘Overloading’이라는 영어 단어를 그대로 번역하면 ‘과적’이라고 해석할 수 있을 것 같은데, 위 사진 속 트럭이 여러 가지 기능들을 싣고 다니는 하나의 메서드라고 볼 수 있죠.

 

 

📃 2. Why Overloading?

그렇다면 오버로딩은 왜 필요할까요? 같은 이름으로 다른 기능을 하는 메서드들을 만들어두면 헷갈리지는 않을까요?

 

사실 위와 같은 생각도 틀린 생각은 아닙니다. 오버로딩을 잘못 남발하게 되면 다른 사람이 내 코드를 볼 때 더 헷갈릴 수도 있고, 예기치 못한 에러를 발생시킬 수도 있습니다.

 

하지만 반대로 생각하면 오버로딩을 적절히 잘 사용할 땐 아래와 같은 장점을 얻을 수 있습니다.

1) 같은 기능을 하는 메서드를 하나의 이름으로 사용 가능하다.

2) 메서드의 Naming에 대한 고민을 덜 수 있다.

 

 

📃 3.  오버로딩 구현

오버로딩을 구현하는 데는 크게 두 가지 방법이 있습니다.

 

첫째는 파라미터의 타입은 동일하게 둔 채 파라미터 개수를 바꾸는 것, 둘째는 파라미터의 타입을 바꾸는 것입니다. 각각에 대한 간단한 예시를 보겠습니다.

 

(1) 파라미터의 개수를 변경

class Adder{  
	static int add(int a,int b){return a+b;}  
	static int add(int a,int b,int c){return a+b+c;}  
}
class TestOverloading1{  
    public static void main(String[] args){  
        System.out.println(Adder.add(11,11));  
        System.out.println(Adder.add(11,11,11));  
    }
}

 

(2) 파라미터의 타입을 변경

class Adder{  
	static int add(int a, int b){return a+b;}  
	static double add(double a, double b){return a+b;}  
}  
class TestOverloading2{  
	public static void main(String[] args){  
		System.out.println(Adder.add(11,11));  
		System.out.println(Adder.add(12.3,12.6));  
	}
}

 

Adder 클래스 내에 add 메서드가 오버로딩 되어 있음을 알 수 있고, 위와 같은 방식으로 원하는 파라미터의 개수와 타입에 따라 적절히 메서드 오버로딩을 사용할 수 있음을 이해하면 될 것 같습니다.

 

👻 메서드의 리턴 타입만 바꾸면 오버로딩인가? - No

오버로딩에 대해 정확하게 이해하지 못하면 메서드의 이름만 같게 만들면 오버로딩이라고 생각할 수 있습니다. 아닙니다.

 

메서드 오버로딩은 리턴 타입만 바꾸는 메서드들에 대해서는 적용되지 않습니다. 엄연히 말하면, 그렇게 하면 컴파일 에러가 나죠.

 

왜 그런가에 대해서는 조금만 생각해보면 됩니다.

 

똑같은 개수와 타입의 파라미터를 동일한 이름의 메서드에 주면, JVM은 어떤 메서드를 지금 실행시킬 것인지 판단할 수가 없기 때문입니다. 컴파일 시점에 코드를 한 번 쓱 훑어보는 JVM 입장에서는 이러한 예견된 에러에 대해 컴파일 에러를 표시하게 됩니다.

 

📚 4. 가변인자(varagrs)와 오버로딩

가변인자란 매개변수의 개수가 동적으로 변할 수 있음을 의미하는 기능이며, '타입... 변수명'과 같은 형식으로 선언합니다.

 

PrintStream클래스의 printf()가 가변인자를 사용하는 대표적인 메서드라고 볼 수 있습니다.

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

위와 같이 가변인자 외에 매개변수가 더 있다면 가변인자를 매개변수 중에서 제일 마지막에 선언해야 하는데, 그렇지 않으면 가변인자인지 아닌지를 구별할 수 없기 때문에 컴파일 에러가 발생합니다.

 

한편, 가변인자는 인자로 아무것도 받지 않을 수도 있고 배열도 받을 수 있는데, 이는 가변인자가 내부적으로 배열을 이용하기 때문입니다.

 

예를 들어, 아래에서 concatenate()는 여러 문자열을 결합해주는 메서드이며, 네 줄 모두 정상적으로 작동합니다.

System.out.println(concatenate());	
System.out.println(concatenate("a"));
System.out.println(concatenate("a","b"));
System.out.println(concatenate(new String[]{"A", "B"));

한편, 가변인자가 선언된 메서드는 호출될 때마다 배열을 새로 생성합니다. 가변인자가 편리하지만 비효율성도 존재하기 때문에 꼭 필요한 경우에만 가변인자를 사용하는 것이 중요한 이유입니다.

 

📚 5. Overloading main() in Java

오버로딩에서 마지막으로 짚어볼 내용은 main() 메서드에 대한 오버로딩입니다. 과연 main() 메서드도 오버로딩이 가능할까요?

 

앞서 살펴봤던 오버로딩이 안 되는 경우에서 그 이유는 JVM이 메서드를 구분할 수 없었기 때문이었습니다.

 

그렇다면 main() 메서드 역시 파라미터의 개수나 타입을 다르게 하면 충분히 오버로딩이 가능하다는 것을 추론할 수 있습니다.

 

실제로 아래 코드는 정상적으로 작동합니다.

import java.io.*;
  
public class Test {
      
    // Normal main()
    public static void main(String[] args) {
        System.out.println("Hi Geek (from main)");
        Test.main("Geek");
    }
  
    // Overloaded main methods
    public static void main(String arg1) {
        System.out.println("Hi, " + arg1);
        Test.main("Dear Geek","My Geek");
    }
    public static void main(String arg1, String arg2) {
        System.out.println("Hi, " + arg1 + ", " + arg2);
    }
}

하지만 알아둬야 할 점은 main() 메서드를 오버로딩한다고 하더라도 JVM은 프로그램 시작을 위한 main() 메서드를 기본적인 형태의 psvm(Public static void main args(String[] args)으로부터 찾는다는 것입니다.

 

따라서 이 main() 메서드는 자동으로 실행되지 않습니다.

 

사실, 굳이 main() 메서드를 오버로딩할 이유가 있을까?하는 궁금증도 있습니다. 그냥 오버로딩의 사용 조건과 개념에 대해 이해하는 차원에서 생각정도는 해보는 것으로 마무리하겠습니다.

 

감사합니다.