Java

오버로딩(Overloading),오버라이딩(Overriding)이란? + 차이점

woo0doo 2023. 2. 12. 23:21

오버로딩 vs 오버라이딩

 

개념

다형성이란 하나의 메서드나 클래스가 있을 때 그것이 다양한 방법으로 동작하는 것을 말하며, 자바에서는 주로 오버로딩(Overloading)과 오버라이딩(Overriding)을 통해서 다형성을 지원한다.

 

오버로딩(Overloading) : 메서드의 이름은 같고 매개변수의 유형과 개수가 다르도록 하는 것을 의미한다.

- 리턴값만을 다르게 갖는 오버로딩은 작성할 수 없다.

 

오버라이딩 (Overriding) : 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해서 사용하는 것을 의미한다.

-메서드의 이름은 물론 파라미터의 개수나 타입도 동일해야 하며, 주로 상위 클래스의 동작을 상속받는 하위 클래스에서 변경하기 위해 사용된다.

 

 

간략하게 요약하면,

 

  • 오버로딩(Overloading)은 기존에 없던 새로운 메서드를 정의하는 것이고,
  • 오버라이딩(Overriding)은 상속받은 메서드의 내용만 변경하는 것이다.

 

오버로딩 예제

class OverloadingMethods {
    public void print() {
        System.out.println("매개변수X - 오버로딩1");
    }

    String print(Integer a) {
        System.out.println("Integer - 오버로딩2");
        return a.toString();
    }

    void print(String a) {
        System.out.println("String - 오버로딩3");
        System.out.println(a);
    }

    String print(Integer a, Integer b) {
        System.out.println("Integer, Integer - 오버로딩4");
        return a.toString() + b.toString();
    }

}

class OverloadingTest {

    public static void main(String[] args) {
        OverloadingMethods om = new OverloadingMethods();

        om.print();
        System.out.println(om.print(3));
        om.print("Hello!");
        System.out.println(om.print(4, 5));
    }
}

 

 

실행결과

 

 

주의해야 할 점

class OverloadingMethods {
    public void print() {
        System.out.println("매개변수X - 오버로딩1");
    }
    public int print() {
    	System.out.println("매개변수X - 오버로딩2");
        return 1;
    }

    String print(Integer a) {
        System.out.println("Integer - 오버로딩3");
        return a.toString();
    }

    void print(String a) {
        System.out.println("String - 오버로딩4");
        System.out.println(a);
    }

    String print(Integer a, Integer b) {
        System.out.println("Integer, Integer - 오버로딩5");
        return a.toString() + b.toString();
    }

}

 

위의 코드에서 에러를 유발하는 곳이 있다.

한번 그 코드를 찾아보자.(바로 아래에 정답이 있음)

 

 

 


 

 

 

 

 

 

 

 

정답

 

 

 

아까 처음에 말했듯이 리턴값만 다른 것은 오버로딩을 할 수 없다. 따라서 print()라는 매개변수가 존재하지 않는 메서드가 두 개 존재하기 때문에 에러를 유발한다.

 

결국 오버로딩은 매개변수의 차이로만 구현할 수 있다는 것이다. 매개변수가 다르다면 리턴 값은 다르게 지정할 수 있다.

다시 한번 강조하지만 리턴 값만 다른 것은 오버로딩 할 수 없다.

 


오버로딩을 사용하는 이유

  • 같은 기능을 하는 메서드를 하나의 이름으로 사용할 수 있기 때문이다.
  • 메서드의 이름을 절약할 수 있기 때문이다.

많이 사용하는 println() 메서드를 예로 들면 이해하기 쉽다. println() 메서드는 오버로딩 되어있기 때문에 int형 인자, string형 인자, boolean형 인자, char형 인자 모두 받아서 동작할 수 있다.

 

만약 오버로딩이 없다면, int형 인자를 받는 메서드는 printlnInt()로 String형 인자를 받는 메서드는 printInString()으로 boolean형 인자를 받는 메서드는 printInBoolean()처럼 각각의 메서드의 이름을 따로 만들어줘야 한다.

오버로딩이 있기 때문에 같은 기능을 하는 메서드를 하나의 이름은 printIn()으로 사용할 수 있게 된다.

 

 


 

오버라이딩

 

 

오버라이딩의 조건

 

 상위 클래스가 가지고 있는 멤버변수가 하위 클래스로 상속되는 것처럼 상위 클래스가 가지고 있는 메서드도 하위 클래스로 상속되어 하위 클래스에 사용할 수 있다. 또한 하위 클래스에서 메서드를 재정의해서도 사용할 수 있다.

쉽게 말해 메서드의 이름이 서로 같고, 매개변수가 같고, 반환형이 같을 경우에 상속받은 메서드를 덮어쓴다고 생각하면 된다. '부모 클래스의 메서드는 무시하고, 자식 클래스의 메서드 기능을 사용하겠다'와 같다. 

 

오버라이딩 예제

 

class SuwonMember {
    String test() {
        return "저는 수원에 거주중입니다.";
    }
}

class SuwonStudent extends SuwonMember{
    String test() {
        return "저는 YD 입니다.";
    }
}

public class OverridingTest {

    public static void main(String[] args) {
        SuwonStudent student = new SuwonStudent();

        System.out.println(student.test());
    }


}

 

 

 

결과

 

SuwonMember에서 정의한 test() 메서드의 값 -> '저는 수원에 거주중입니다'가 아닌

SuwonStudent에서 재정의한 값 -> '저는 YD 입니다.' 가 출력되는 것을 확인할 수 있다.

 

주의해야 할 점

 

  • 자식 클래스에서 오버라이딩하는 메소드의 접근 제어자는 부모 클래스보다 더 좁게 설정할 수 없다

만약 부모클래스에서 default로 설정되어 있다면, 자식 클래스는 default 보다 같거나 더 넓은 범위의 접근 제어자만 설정할 수 있으므로 default, protected, public의 접근 제어자를 사용할 수 있다.

 

  • 예외(Exception)는 부모 클래스의 메소드보다 많이 선언할 수 없다.

부모 클래스에서 어던 예외를 throws 한다고 하면, 자식 클래스에서는 그 예외보다 더 큰 범위의 예외를 throws 할 수 없다.

  • static메서드를 인스턴스의 메서드로 또는 그 반대로 바꿀 수 없다.

 

상위 클래스의 static 메서드는 클래스에 속하는 메서드이기 때문에 상속되지 않고 따라서 오버라이드 되지도 않는다. (static 메서드는 런타임 시에 생성되는 것이 아니라 컴파일 시 생성되어 메모리에 적제 되는 방식이기 때문에 런타임 시에 해당 메서드를 구현한 실체 객체를 찾아가서 호출하게 된다. static 메서드에 대해서는 다형성이 적용되지 않는다.)

다음과 같이 static을 붙인 메서드에서는 에러를 유발한다.

 

 

final이 지정된 메서드 역시 오버라이드가 불가하며, private 접근 제어자를 가진 메서드는 상속 자체가 불가능하기 때문에 오버라이드가 성립되지 않는다.

(final의 경우 하위 클래스가 해당 메서드를 재정의 할 수 없도록 하기 위해서 사용된다.)

 

 


@Override 어노테이션을 쓰는 이유

 

@Override 어노테이션은 없어도 오버라이딩이 적용되어 정상적으로 잘 동작한다. 그렇다면 @Override 어노테이션을 왜 쓸까?

 

1. @Override 어노테이션은 시스템에서 오버라이딩한 메서드라고 알리는 역할로 오버라이딩이 잘못된 경우 경고를 준다.

 

예를 들어 백엔드 단에서 사용되는 라이브러리 중 하나가 업데이트되어 상속하는 클래스 메서드의 시그니처가 바뀌었다.

@Override 어노테이션이 적용되지 않은 상태에서는 전에 오버라이드 한 메서드가 업데이트 이후 그냥 추가적인 메서드로 인식되어 컴파일 오류가 발생하지 않는다. 이때 @Override 어노테이션을 적용함으로써 의도적으로 컴파일 오류를 일으켜 작동방식이 바뀌는 것을 대비할 수 있다.

 

 

2. @Override를 표시함으로써 코드 리딩 시에 해당 메서드가 오버라이딩하였다는 것을 쉽게 파악할 수 있다는 장점이 있다.

 

 


정리 - 오버로딩 vs 오버라이딩

 


 

참고

 

https://hyoje420.tistory.com/14

https://wildeveloperetrain.tistory.com/110

https://lnsideout.tistory.com/entry/JAVA-%EC%9E%90%EB%B0%94-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9-%EA%B0%9C%EB%85%90-%EC%99%84%EB%B2%BD%EC%A0%95%EB%A6%AC