Programming/Java

[자바] 오버로딩(Overloading)과 오버라이딩(Overriding)의 차이점

bu119 2023. 7. 9. 15:00
728x90
반응형

객체지향 프로그래밍에서 다형성을 설명할 때,

하나의 클래스 내부에 같은 이름의 기능을 여러 개 정의하거나 상위 클래스의 기능을 하위 클래스에서 다시 정의하는 것,

즉, 오버라이딩과 오버로딩에 대해서 간략하게 짚고 넘어갔었다.

 

이번에는 오버라이딩과 오버로딩이 구체적으로 어떻게 사용되는지. 또, 어떤 차이점을 가지고 있는지에 대해서 알아보자.

 

오버로딩(Overloading)과 오버라이딩(Overriding)

 

다형성

다형성이란,
하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작하는 것을 말한다.

 

자바에서는 주로 오버로딩(Overloading)과 오버라이딩(Overriding)을 통해서 다형성을 지원한다.

 

오버로딩(Overloading) - 확장

하나의 클래스 내부에서 확장하는 개념

 

오버라이딩(Overriding) - 재정의

부모 클래스의 상속을 받은 자식 클래스에서 확장하는 개념

 

 

예시

키보드나 스마트폰을 예를 들면,

키보드는 같은 버튼의 동작 방법을 가지고 있더라도
ESC는 취소, Enter는 실행의 목적을 가지고 있다.

스마트폰의 키패드 또한 문자나 게임 등의 모양은 같지만
다른 기능들을 수행하는 것들을 말한다.

 

오버로딩과 오버라이딩은 이름이 비슷하여 헷갈릴 수 있지만,

전혀 다른 개념이기 때문에 헷갈리지 않도록 차이점을 확실히 알아두는 것이 좋다.

 

오버로딩(Overloading)

같은 이름의 메서드 여러 개를 가지면서 매개변수의 유형과 개수가 다르도록 사용하는 것이다.

 

  • 메소드의 이름이 일치해야 한다.
  • 메소드 매개변수의 개수 또는 타입이 달라야한다.
    • 개수가 같다면 타입, 타입이 같다면 개수를 다르게 해야한다.
    • 다양한 유형의 호출에 응답할 수 있다.
  • 메소드의 return 타입이 달라야 한다.

 

오버로딩의 특징

  • 접근 제어자를 자유롭게 지정해줄 수 있다.
    • 접근 제어자를 public, default, protected, private으로 다르게 지정해줘도 상관없다.
    • 하지만 같은 매개변수 개수와 타입을 가지면서 접근 제어자만 다르게 한다고 오버로딩이 되지는 않는다.
    • 또한 매개변수는 같고 반환 타입이 다른 경우는 오버로딩이 성립되지 않는다.

 

일반적으로 하나의 클래스 안에 같은 이름의 메서드를 정의하게 되면 에러가 발생한다.

 

 

예제 코드)

public class Test {

    // 매개변수가 없는 overloadingTest() method
    void overloadingTest(){
        System.out.println("매개변수를 받지 않는 메서드");
    }

    // 매개변수로 int형 인자 2개를 요청하는 overloadingTest(int a, int b) method
    void overloadingTest(int a, int b){
        System.out.println("int형 인자 2개를 요청하는 메서드 "+ a + ", " + b);
    }

    // 매개변수로 String형 인자 1개를 요청하는 overloadingTest(String str) method
    void overloadingTest(String str){
        System.out.println("String형 인자 1개를 요청하는 메서드 " + d);
    }
}

함수를 실행시키면,

public class OverTest {
 
    public static void main(String[] args) {
        
        // Test 객체 생성
        Test te = new Test();
        
        // 1. 매개변수가 없는 overloadingTest() 호출
        te.overloadingTest()
        // 실행 결과: "매개변수를 받지 않는 메서드"
        
        
        // 2. 매개변수가 int형 두개인 overloadingTest() 호출
        te.overloadingTest(20, 80);
        // 실행 결과: "int형 인자 2개를 요청하는 메서드 20, 80"
        
        
        // 3. 매개변수가 String 한개인 overloadingTest() 호출
        te.overloadingTest("오버로딩 예제");
        // 실행 결과: "String형 인자 1개를 요청하는 메서드 오버로딩 예제"
        
    }
 
}

 

오버로딩하나의 클래스에서 같은 이름의 메소드들을 여러 개 가질 수 있다는 점이 중요한 포인트다.

 

예시

자바의 Sytem.out.println() 문이나 파이썬의 print() 문같은 출력문을 예를 들면,

 

이런 출력문들은 기본적으로 언어에서 클래스를 내장해놓았기 때문에

어떤 타입의 매개변수를 넣더라도 출력 값을 모두 콘솔에 띄울 수 있다.

 

System.out.println(1+1) 이나 String test = "문자"라고 변수를 정의하고
이 test 를 출력하면 콘솔에서는 숫자 2나 "문자"가 출력된다.

 

매개변수가 문자형이든 정수형이든 같은 이름의 메소드를 안에 여러개 정의해놨기 때문에

매개변수의 타입에 따라 "어? 정수형이 들어왔네? 그럼 이 메소드","어? 문자형이 들어왔네? 그럼 저 메소드" 이런 식으로 다르게 출력할 수 있다.

 

오버로딩을 사용하는 이유

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

 

예시

println() 메서드를 예를 들면,

 

println()

println() 메서드는 오버로딩 되어있기 때문에
int형 인자, String형 인자, boolean형 인자, char형 인자 모두 받아서 동작할 수 있다. 

 

만약 오버로딩이 없다면,

int형 인자를 받는 메서드는 printlnInt()로
String형 인자를 받는 메서드는 printlnString()으로
boolean형 인자를 받는 메서드는 printlnBoolean() 처럼 각각의 메서드 이름을 따로 만들어줘야 한다.

 

오버로딩이 있기 때문에 같은 기능(콘솔로 인자를 출력하는)을 하는 메서드를 하나의 이름인 println()으로 사용할 수 있다. 

 

 

오버라이딩(Overriding)

상위 클래스(부모 클래스)의 메소드를 하위 클래스(자식 클래스)에서 재정의하는 것이다.

(상위 클래스로부터 상속받은 메서드의 동작만을 재정의한다.)

 

상위 클래스가 가지고 있는 멤버 변수가 하위 클래스로 상속되는 것처럼

상위 클래스가 가지고 있는 메서드도 하위 클래스로 상속되어 하위 클래스에 사용할 수 있다. 

(상속받은 메서드를 그대로 사용할 수도 있지만, 필요에 따라 메서드를 재정의하여 사용하는 경우가 있다.)

 

즉, 부모 클래스가 물려준 메소드를 자식 클래스에서 조금 변형해서 사용하고 싶다면,

오버라이딩을 사용하여 자유롭게 기능을 추가 또는 재정의를 해서 사용할 수 있다.

 

단, 아래 조건을 충족해야 한다.

  • 메소드의 이름이 일치해야 한다.
  • 메소드 매개변수의 개수, 순서 그리고 데이터 타입 일치해야 한다.
  • 메소드의 return 타입이 일치해야 한다.

 

오버라이딩의 특징

  1. 상위 클래스의 메서드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다.
  2. 상위 클래스의 메서드보다 더 큰 범위의 예외를 선언할 수 없다.
  • 상위 클래스의 static 메서드는 클래스에 속하는 메서드이기 때문에 상속되지 않고, 오버라이드 되지도 않는다.
    • static 메서드에 대해서는 다형성이 적용되지 않다.
  • final이 지정된 메서드 역시 오버라이드를 할 수 없다.
    • final은 하위 클래스가 해당 메서드를 재정의 할 수 없도록 하기 위해서 사용된다.
  • private 접근 제어자를 가진 메서드는 상속 자체가 불가능하기 때문에 오버라이드가 성립되지 않다.
  • 실무에서 인터페이스를 implements로 가져와서 인터페이스에 정의된 메서드를 @Override 어노테이션을 사용하여 오버라이딩해서 재정의한다. 이때 interface의 메서드를 오버라이드해서 구현하는 경우 반드시 public 접근 제어자를 사용해야 한다.

 


참고

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

@Override 어노테이션은 없어도 오버라이딩이 적용되어 정상적으로 동작한다.

그렇다면 @Override 어노테이션을 쓰는 이유는 무엇일까?

 

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

 

예를 들어,

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

@Override 어노테이션이 적용되지 않은 상태에서는
전에 오버라이드 한 메서드가 업데이트 이후 그냥 추가적인 메서드로 인식되어 컴파일 오류가 발생하지 않는다.


이때 @Override 어노테이션을 적용함으로써, 의도적으로 컴파일 오류를 일으켜 작동방식이 바뀌는 것을 대비할 수 있다.
또한, @Override를 표시함으로써, 코드 리딩 시에 해당 메서드가 오버라이딩하였다는 것을 쉽게 파악할 수 있다는 장점이 있다.


 

예제 코드)

public class Parent {
    public void overridingTest() {
        System.out.println("부모 메서드의 내용");
    }
}

부모 클래스의 메서드를 상속받아 재정의

public class Child extends Parent {
    @Override
    public void overridingTest() {
        System.out.println("부모 클래스의 메서드를 상속받아 내용을 재정의해서 사용");
    }
}

 

오버라이딩은 상위 클래스의 메소드를 하위 클래스에서 재정의한다는 점이 중요포인트이다.

 

쉽게 말해서,

"메서드의 이름이 같고, 매개변수가 같고, 반환형이 같은 경우에 상속받은 메서드를 덮어쓴다" 라고 생각할 수 있다.

 

 

예시

스프링 프레임워크를 다루면서,
인터페이스에 임시로 정의해 두었던 것을 하위 클래스에서 implements로 가져와
@Override 어노테이션을 사용하여 오버라이딩하면서 재정의할 수 있다.

 

다시말해,

인터페이스에서 상위 클래스로 임시로 해당하는 메소드를 정의해두고, 그걸 오버라이딩으로 하위 클래스에서 재정의해서 사용한다. 어찌보면 같은 이름의 메소드를 다른 기능으로 고쳐쓴다고 이해하면 편하다.

 

오버로딩과 오버라이딩의 차이점

오버로딩은 매게변수나 타입이 달라야하지만 오버라이딩은 동일해야하고,

오버로딩은 리턴 타입이 무엇이든지 상관없지만 오버라이딩은 동일해야한다는 차이점이 있다.

 

오버로딩(Overloading)과 오버라이딩(Overriding) 성립조건

구분  오버로딩  오버라이딩 
 메서드 이름 동일  동일 
 매개변수, 타입 다름  동일 
 리턴 타입 상관없음  동일 

 


참고 자료

https://private.tistory.com/25

https://metafor.notion.site/54d624628a634c879cc93d94f54cd2d1#6f29297c9e0a4ef3a67538c5caa1cdca

https://velog.io/@god1hyuk/Javaclass-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9overriding%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9overloading

https://wildeveloperetrain.tistory.com/110

728x90
반응형

'Programming > Java' 카테고리의 다른 글

[자바] 추상클래스와 인터페이스의 차이  (0) 2023.07.13