Post

면접준비 - Java 오버로딩, 오버라이딩

오타, 지적 환영입니다.

메소드 시그니처(method signature)

오버로딩, 오버라이딩을 공부하기 전에 메소드 시그니처에 대해 알아야한다.

메소드 시그니처란 메소드의 선언부에 명시되어 있는 매개변수의 리스트다. 나는 파라미터가 더 이해하기 편해서 파라미터라 하겠다.

다음 조건을 만족시 같은 시그니처를 갖는다

  • 메소드 이름
  • 파라미터 수
  • 파라미터 타입의 순서

리턴 타입은 포함 하지 않는다고 한다. 즉,

1
2
3
4
5
6
7
8
9
public class Test{
    public int calcu(int x,int y){
        return x+y;
    }

    public void calcu(int x,int y){
        return ;
    }
}

는 같은 시그니처를 갖기에 둘 중 하나는 선언할 수 없다.


메소드 오버로딩(Method Overloading)

그러면 위의 조건에서 메소드 이름은 같지만 파라미터 수나 파라미터 타입의 순서가 바뀌게 선언해도 될까?

가능하다. 메소드 오버로딩은 이렇듯 같은 이름의 메소드를 중복하여 정의하는 것을 말한다.

1
2
3
4
5
6
7
8
9
public class Test{
    public int calcu(int x,int y){
        return x+y;
    }

    public int calcu(int x){
        return x;
    }
}

결론

단점

  • 비슷한 기능으로 사용하지 않으면 협업시에 혼동을 줄 수 있다.

장점

  • 메소드 이름을 몇가지만 기억할 수 있다. (매개변수 형태만 다른것들을 SumInt(), SumLong()이렇게 안 지어도 된다는 것.)
  • 위로 인해서 Sum()으로만 선언할 수 있으므로 메소드 길이가 짧아질 수 있다.
  • 이로인해서 기능을 예측하기가 쉽다.
  • 파라미터를 다양하게 받아서 처리할 수 있다.
  • 결국 코드의 가독성이 향상한다.

특이사항

오버로딩의 메소드 호출 대상은 컴파일시점에 결정되기에 원하는대로 작동하지 않을 수 있다. 그렇기에 오버로딩은 정적으로 선택된다.

Reference


오버라이딩

오버라이딩은 상위 클래스가 가지고 있는 메소드들을 하위 클래스에서 재정의하는 것이다.

상속 관계에 있는 클래스 간 메소드 재정의를 말한다.

조건

부모 클래스 메소드와 동일한 시그니처 + 메소드 리턴타입을 가져아한다.

  • 접근지정자 빼고 모든것이 같다고 생각하면 된다.
  • 접근지정자는 부모클래스보다 좁은 범위로는 지정할 수 없다.
  • 부모 클래스의 메서드보다 더 많은 예외를 선언할 수 없다.

필요성

  • 오버라이딩을 하지 않고 이름을 짓는다면 다른 메소드를 호출할 위험이 존재하고, 다른 이름을 짓는다는것 자체가 문제다.
  • 추가로 @Override라는 어노테이션을 추가하여 오버라이딩된 메소드라고 명시할 수 있다.
  • 자식 클래스에서 상황에 맞게 오버라이딩 해야하는 경우도 있기에 필요하다.

메소드 디스패치

메소드 디스패치란 어떤 메소드를 호출할지 결정하여 실제로 실행하는 것을 의미한다.

컴파일 시점에 정하는 것을 정적 메소드 디스패치라 하고, 런타임 시점에 정하는 것을 동적 메소드 디스채리라고 한다.

정적 메소드 디스패치

  • 컴파일 시점에 어떤 메소드를 호출할지 명확히 알고 있는 경우이다.
  • 컴파일 시 생성된 바이트 코드에도 이 정보가 그대로 남아있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Parent {
    public void method1(){
        System.out.println("Parent method1입니다");
    }
}

public class Child extends Parent{
    @Override
    public void method1() {
        System.out.println("Child method1입니다");
    }
}

public class Main {
    public static void main(String[] args){
        Child child = new Child();
        child.method1(); //정적 메소드 디스패치
    }
}

명확하게 인스턴스 타입을 지정하고 @Override로 오버라이딩된 메소드도 알 수 있다. 이와 같은 예시를 말한다.

다이나믹 메소드 디스패치

  • 동적 메소드 디스패치는 컴파일 시점에 어떤 메소드를 호출할 지 몰라서 런타임때 정하는 것을 말한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Dispatch {

	static abstract class Service {
		abstract void run();
	}

	static class MyService extends Service {
		@Override
		void run() {//(1)
			System.out.println("MyService.run()");
		}
	}


	static class YourService extends Service {
		@Override
		void run() {//(2)
			System.out.println("YourService.run()");
		}
	}


	public static void main(String[] args) {
		Service service = new MyService();

		
		// = new MyService() 부분을 무시하고 아래 코드만 본다고 하자
		service.run();
	}
}

부모 클래스이자 추상 클래스인 Service 타입을 선언하고 run()을 호출하여 컴파일 시점에는 어떤 메소드가 수행될 지 알수 없다. 하지만, 예상하듯이 (1)의 함수가 호출될 것인데, 이때 발생하는 것이 동적 메소드 디스패치이다.

어떻게 이런 방식이 가능하냐면 run()메소드 호출당시 receiver parameter가 전달되는데 이는 클래스의 this에 해당되는 Object이다. 그래서 MyService가 전달되고 (1)이 호출된다.

Refrence

This post is licensed under CC BY 4.0 by the author.