Java

오버로딩 & 오버라이딩의 결정 시점 차이 (Overloading & Overriding)

성만이 2020. 2. 6. 13:18

오버로딩(Overloading)오버라이딩(Overriding)은 객체지향의 특징 중 하나인 다형성을 지원하는 방법입니다.

 

아시듯이, 

오버로딩(Overloading)은 한 클래스 내에서 각각 다른 파라미터를 가진 동명의 메소드를 정의한 것이고,

오버라이딩(Overriding)은 상속된 클래스에서 부모 클래스에 존재하는 메소드를 재정의한 것이지요.

 

이 두 개념을 자바에서 구현할 때 차이점이 있습니다. 특히 오버로딩을 사용할 때 주의해서 사용해야 합니다.

오버로딩과 오버라이딩된 동명의 메소드들 중에서 사용할 메소드를 결정하는 시점에 차이가 있습니다.

 


1. 오버로딩(Overloading) & 오버라이딩(Overriding) 메소드 결정 시점 (JAVA)

- 오버로딩된 메소드는 컴파일(Compile) 시에 어떤 메소드를 실행할지 결정됩니다.

- 오버라이딩된 메소드는 런타임(Runtime) 시에 어떤 메소드를 실행할지 결정됩니다.

 


2. Example

바로 아래의 예제를 통해 확인해 보도록 하겠습니다.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

public class Overload {
	public static void get(List<?> list) {
		System.out.println("List");
	}
	
	public static void get(ArrayList<?> list) {
		System.out.println("ArrayList");
	}
	
	public static void get(LinkedList<?> list) {
		System.out.println("LinkedList");
	}
	
	public static void main(String[] args) {
		List<?>[] listArray = { new ArrayList<>(), new LinkedList<>(), new Vector<>() };
		Arrays.stream(listArray).forEach(list -> get(list));
	}
}

예제는 Overload 클래스 안에 get 메소드가 오버로딩되어 있습니다.

main에서의 결과는 어떨까요? 

 

- ArrayList로 초기화하여 get 메소드를 호출하면 "ArrayList"

- LinkedList로 초기화하여 get 메소드를 호출하면 "LinkedList"

- Vector로 초기화하여 get 메소드를 호출하면 "List"

라고 추측하지만 이는 정답이 아닙니다. 모두 "List"의 결과를 화면에 반환합니다.

 

그러면 오버라이딩된 메소드를 호출할 때는 어떨까요?

import java.util.ArrayList;
import java.util.Arrays;

public class MyList<E> extends ArrayList<E> {
	private static final long serialVersionUID = 1L;

	/* (non-Javadoc)
	 * @see java.util.ArrayList#get(int)
	 */
	@Override
	public E get(int index) {
		System.out.print("MyList get method returns = ");
		return super.get(index);
	}

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		ArrayList<String>[] listArray = new ArrayList[2];
		listArray[0] = new ArrayList<String>();
		listArray[1] = new MyList<String>();
		Arrays.stream(listArray).forEach(list -> list.add("first object"));
		Arrays.stream(listArray).forEach(list -> System.out.println(list.get(0)));
	}
}

MyList 클래스를 만들어 ArrayList를 상속받아 get 메소드를 오버라이딩합니다.

ArrayList<String> 배열의 0번에는 ArrayList 클래스를, 1번에는 MyList 클래스를 두었습니다.

그리고 문자열을 추가한 뒤 get 메소드를 통해 출력하면 결과는 어떨까요?

 

- ArrayList의 get 메소드의 결과를 출력하면 당연히 추가했던 문자열 "first object"

- MyList의 get 메소드의 결과를 출력하면 재정의한대로 "MyList get method returns = first object"

와 같은 결과를 볼 수 있습니다.

 


3. Why ?

이러한 결과가 나온 이유는 오버로딩과 오버라이딩 메소드의 결정 시점의 차이 때문입니다.

컴파일 시점에서 List 클래스 배열인 listArray의 각 List 객체들이 어떤 List 클래스로 초기화될 지 알 수 없습니다.

때문에 컴파일 시점에서 메소드가 결정되는 오버로딩의 경우, List 클래스의 파라미터를 갖는 메소드로 결정됩니다.

반대로, 런타임 시점에서 new 연산자를 통해 실제 ArrayList, MyList 클래스의 객체로 생성되었음을 알 수 있습니다.

때문에 런타임 시점에서 메소드가 결정되는 오버라이딩의 경우, 해당 클래스의 메소드가 실행될 수 있습니다.

 

 

오버로딩의 이러한 동작 방식은 사용할 때 혼란을 줄 수 있습니다. 그렇기 때문에 상속된 클래스의 파라미터를 갖도록 오버로딩하는 것을 피하고, 같은 자료형의 조합 혹은 같은 수의 파라미터를 갖는 오버로딩 또한 사용하지 않는 것이 매끄러운 개발 진행에 도움이 될 것이라고 생각합니다.