[북스터디]자바 기술 면접 유명 질문 (맛보기) 오버라이딩 vs.오버로딩

Wang_Seok_Hyeon·2023년 9월 6일
0
post-thumbnail

Intro

오버라이딩 vs. 오버로딩 이 다형성인가요?
이 질문에 라고 자신있게 답변하는 분도 적으며 각각 어떤 다형성인가요?
라는 질문은 더더욱 답변하기 어려운 질문이 바로 해당 주제다.
우선, 답을 보자면 간단하게 이야기하면

오버라이딩 == 런타임 다형성 vs. 오버로딩 == 컴파일 다형성

위의 인용문에 들어간 핵심 내용을 머릿속에 넣고 간단하게 이에 관한 맛보기 질문 2가지와 연관질문 7가지를 알아보자!

객체지향 프로그래밍에서 메서드 오버라이딩이란 무엇인가?

  1. 메서드 오버라이딩은 객체지향 프로그래밍 기법으로, 개발자가 같은 이름과 시그니처를 가지면서 다르게 동작하게 2개의 메서드를 작성하는 것을 의미합니다. 이때 메서드는 static, pri-vate,final 메서드가 아니어야 합니다. 메서드 오버라이딩은 상속 혹은 런타임 다형성에 사용할 수 있습니다.
  2. 상속에서는 슈퍼클래스에 어떤 메서드(또는 오버라이드된 메서드)가 있을 때 서브클래스에서 이 메서드를 오버라이딩할 수 있습니다. 이러한 메서드를 오버라이딩 메서드라고 합니다. 런타임 다형성에서는 인터페이스에 어떤 메서드가 있을 때 이 인터페이스를 구현하는 클래스가 해당 메서드를 오버라이딩합니다.
  3. 자바는 객체 타입에 따라 해당 메서드를 런타임에 결정합니다. 메서드 오버라이딩은 유연하고 확장 가능한 코드를 유지합니다. 즉, 최소한의 코드 변경으로 새로운 기능을 추가할 수 있게 합니다.

세부적인 내용이 추가로 요구된다면 메서드 오버라이딩과 관련된 주요 규칙을 나열하자.

  • 메서드 이름과 시그니처(동일한 반환 타입 혹은 하위 타입 포함)는 슈퍼클래스와 서브클래스 또는 인터페이스와 구현 클래스에서 동일합니다.
  • 같은 클래스에 있는 메서드를 오버라이드 할 수 없습니다.(하지만, 같은 클래스에 있는 메서드를 오버로드 할 수는 있습니다.)
  • private, static, final 메서드는 오버라이드 할 수 없습니다.
  • 오버라이딩 메서드는 오버라이드된 메서드의 접근성을 감소시킬 수 없지만 그 반대는 가능합니다.
  • 오버라이딩 메서드는 오버라이드된 메서드에 의해 발생한 확인된 예외보다 Exception 클래스 계층 구조에서 더 상위에 있는 예외를 발생시킬 수 없습니다.
  • 오버라이딩 메서드에는 항상 @Override 어노테이션을 사용해야 합니다.(권장사항이지, 없어도 무방하긴 합니다.)

오버라이딩 메서드 예제는 아래와 같습니다. 총 두 가지를 다루고자 합니다. 상속 받는 것과 인터페이스를 구현하는 오버라이딩이며 상속을 다루고 인터페이스를 다루고 이를 실행하는 Main함수를 보도록 하겠습니다.

먼저 부모 클래스를 만듭니다.

public class Parent {
    public void execute() {
        System.out.println("Execute parent code ...");
    }
}

이를 상속 받는 자녀 클래스를 만듭니다. 자바에서 상속은 extends를 사용합니다.

public class Child extends Parent {
    @Override
    public void execute() {
        // super.execute(); - 슈퍼클래스의 코드를 먼저 실행하려는 경우
        System.out.println("Execute child code ...");
    }
}

다음은 인터페이스를 만들고 이를 구현한 클래스를 만들어 보겠습니다.

public interface Base {
    public void execute();
}
public class Concrete implements Base {
    @Override
    public void execute() {
        System.out.println("Execute concrete code ...");
    }
}

마지막으로 이 두 가지의 오버라이딩을 테스트하기 위한 메인 함수를 포함한 클래스를 작성합니다.

public class Main {
    public static void main(String[] args) {
        // 상속의 메서드 오버라이딩
        Parent parent = new Parent();
        Child child = new Child();

        parent.execute();
        child.execute();

        // 런타임 다형성의 메서드 상속
        Concrete concrete = new Concrete();
        concrete.execute();
    }
}

실행 결과는 아래와 같습니다.

Execute parent code ...
Execute child code ...
Execute concrete code ...

맛보기인데도 맵다는 생각이 드네요... 다음은 오버로딩에 관해 알아 보고자 합니다!

객체지향 프로그래밍에서 메서드 오버로딩이란 무엇인가?

메서드 오버로딩은 객체지향 프로그래밍 기법으로, 개발자가 같은 이름이지만 다른 시그니처와 다른 기능을 하도록 2개의 메서드를 작성하는 것을 의미합니다. 이때 두 메서드는 모두 static이거나 static이 아니어야 합니다. 다른 시그니처를 가진다는 것은 인수(argumnet)의 개수, 인수의 타입, 인수의 순서가 다르다는 것을 의미합니다. 반환 타입은 메서드 시그니처에 포함되지 않습니다. 따라서 2개의 메서드가 동일한 시그니처를 가지지만 반환 타입이 다른 경우에는 유효한 메서드 오버로딩이 아닙니다. 메서드 오버로딩 기법은 static 혹은 static이 아닌 메서드를 같은 이름이지만 다른 입력값을 가지도록 구현할 수 있도록 하는 강력한 기술입니다. 컴파일러가 오버로딩된 메서드 호툴을 실제 해당하는 메서드로 연결해줍니다. 따라서 런타임 동안 이러한 연결이 이루어지는 것이 아닙니다. 메서드 오버로딩의 유명한 예가 System.out.println입니다. println메서드에는 여러 가지 오버로딩 메서드가 있습니다.

메서드 오버로딩과 관련한 네 가지 주요 규칙은 다음과 같습니다.

  • 오버로딩은 메서드의 시그니처를 변경하여 구현할 수 있습니다.
  • 반환 타입은 메서드 시그니처에 포함되지 않습니다.
  • private, static, final 메서드를 오버로드할 수 있습니다.
  • 같은 클래스에 있는 메서드를 오버로드할 수 있습니다.(하지만 같은 클래스에 있는 메서드를 오버라이드 할 수는 없습니다*)

세부적인 내용이 추가로 요구된다면 예제 코드를 아래와 같이 작성합니다. foozzy의 메서드의 매개변수의 개수, 순서, 유형이 다른 것을 볼 수 있습니다.

단, 반환값이 바뀌는 것으로는 오버로딩이 성립하지 않습니다.

public class Foo {
    public void foozzy(String p, int q) {
        System.out.println("Called foozzy(" + p + ", " + q + ")");
    }

    // 인수의 개수가 다름
    public void foozzy(String p, int q, int w) {
        System.out.println("Called foozzy(" + p + ", " + q + ", " + w + ")");
    }

    // 인수의 순서가 다름
    public void foozzy(int q, String p) {
        System.out.println("Called foozzy(" + q + ", " + p + ")");
    }

    // 인수의 유형이 다름
    public void foozzy(int p, int q) {
        System.out.println("Called foozzy(" + p + ", " + q + ")");
    }

    // 유효하지 않음 - 다른 반환 유형
    /*
    public boolean foozzy(String p, int q) {
        System.out.println("Called foozzy(" + p + ", " + q + ")");
    }
    */
}

Main 클래스를 아래와 같이 작성해 테스트 해 봅니다.

public class Main {
    public static void main(String[] args) {
        Foo foo = new Foo();

        foo.foozzy("text", 1);
        foo.foozzy(1, "text");
        foo.foozzy(-1, 1);
        foo.foozzy("text", -1, 1);
    }
}

실행결과는 아래와 같습니다.

Called foozzy(text, 1)
Called foozzy(1, text)
Called foozzy(-1, 1)
Called foozzy(text, -1, 1)

이 개념들은 알고 있다고 해도 오버라이딩 이외에는 구체적으로 사용할 일이 잘 없었던 것 같기도 해서 미묘한 부분이 있긴 하다. 생성자의 경우도 나중에는 빌더 패턴을 쓰기 때문에 부분적으로 이를 사용한다고 생각하면 된다.

오버라이딩과 오버로딩 관련 추가 질문들

앞에서 언급한 두 가지 질문 외에 해당 질문들과 관련한 다른 몇 가지 질문을 받을 수도 있습니다.

  • 오버로딩과 오버라이딩과 관련한 규칙은 무엇은가?
  • 메서드 오버라이딩과 오버로딩의 주요 차이점은 무엇인가?
  • static이나 private 메서드를 오버라이드 할 수 있는가? → 없다.
  • final 메서드를 오버라이드 할 수 있는가? → 없다. 상속 자체가 안 되기 때문
  • (연관 질문) final 만 메서드 오버라이딩을 방지하는지? → 그렇지 않다. private, static도 마찬가지로 메서드 오버라이딩을 방지한다.
  • static 메서드를 오버로드 할 수 있는가? → 둘 다 static인 경우에 가능
  • 오버라이딩 메서드의 인수 목록을 변경해도 되는가? → 안 된다. 그건 오버로드에 해당.
profile
하루 하루 즐겁게

0개의 댓글