본문 바로가기
Java

[Java; 자바] Java 오버로딩과 오버라이딩: 개념, 차이점, 그리고 스프링부트 활용 예제

by daddydontsleep 2024. 9. 23.
728x90
728x90

사진: Unsplash 의 Soft Rattles

Java에서는 동일한 이름의 메서드를 다양한 방법으로 사용할 수 있는 **오버로딩(Overloading)**과 상속 관계에서 상위 클래스의 메서드를 재정의할 수 있는 **오버라이딩(Overriding)**을 제공합니다. 이 두 개념은 객체지향 프로그래밍에서 중요한 역할을 하며, 코드의 유연성과 재사용성을 높이는 데 기여합니다. 이번 포스트에서는 오버로딩과 오버라이딩의 개념과 차이점을 살펴보고, 이를 이해하기 위한 예제를 통해 차근차근 알아보겠습니다. 더 나아가, 스프링부트(Spring Boot) 환경에서 이를 어떻게 활용할 수 있는지도 알아보겠습니다.

1. 오버로딩(Overloading)이란?

1.1 오버로딩의 정의

오버로딩은 동일한 이름의 메서드를 매개변수의 타입, 개수, 순서를 다르게 하여 여러 개 정의하는 것을 의미합니다. 이를 통해 같은 이름의 메서드를 사용하여 다양한 입력에 대한 처리를 수행할 수 있습니다. 즉, 메서드 이름은 동일하나 인자의 형태에 따라 다른 기능을 수행하도록 정의할 수 있습니다.

1.2 오버로딩이 가능한 조건

메서드 오버로딩은 다음과 같은 조건 중 하나 이상을 만족해야 합니다:

  1. 매개변수의 개수가 다를 때:
    public void display(int a) { };
    public void display(int a, int b) { };


  2. 매개변수의 타입이 다를 때:
    public void display(int a) { };
    public void display(double a) { };


  3. 매개변수의 순서가 다를 때:
    public void display(int a, double b) { };
    public void display(double a, int b) { };


주의
: 리턴 타입이 다르거나 접근 제어자만 다를 경우 오버로딩으로 인식되지 않습니다. 컴파일러는 메서드 시그니처(이름과 매개변수 목록)를 기준으로 오버로딩을 판단합니다.

 

1.3 오버로딩 예제

public class Calculator {
    // 매개변수의 개수가 다른 오버로딩
    public int add(int a, int b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 매개변수의 타입이 다른 오버로딩
    public double add(double a, double b) {
        return a + b;
    }

    // 매개변수의 순서가 다른 오버로딩
    public void print(int a, String message) {
        System.out.println(a + " " + message);
    }

    public void print(String message, int a) {
        System.out.println(message + " " + a);
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 3));           // int 버전 호출
        System.out.println(calc.add(5, 3, 2));        // int, int, int 버전 호출
        System.out.println(calc.add(5.5, 2.3));       // double 버전 호출
        calc.print(10, "Items");                      // int, String 버전 호출
        calc.print("Items", 10);                      // String, int 버전 호출
    }
}

 

위 예제에서 add 메서드는 매개변수의 개수와 타입에 따라 서로 다른 기능을 수행합니다. print 메서드 또한 매개변수의 순서에 따라 오버로딩되어 있습니다.

2. 오버라이딩(Overriding)이란?

2.1 오버라이딩의 정의

오버라이딩은 상속 관계에서 상위 클래스의 메서드를 하위 클래스에서 재정의하여 사용하는 것을 의미합니다. 이를 통해 상위 클래스의 메서드를 하위 클래스의 요구사항에 맞게 수정할 수 있습니다. 오버라이딩된 메서드는 항상 상위 클래스의 메서드와 동일한 이름, 매개변수, 리턴 타입을 가져야 하며, 접근 제어자는 더 넓거나 동일한 수준이어야 합니다.

2.2 오버라이딩의 조건

오버라이딩이 성립하려면 다음 조건을 만족해야 합니다:

  • 메서드 이름, 매개변수 목록, 리턴 타입이 상위 클래스의 메서드와 동일해야 합니다.
  • 접근 제어자는 상위 클래스의 메서드와 같거나 더 넓은 접근 제어자여야 합니다. (예: protected 메서드를 public으로 변경 가능)
  • 메서드의 예외는 상위 클래스 메서드가 던지는 예외보다 좁거나 같아야 합니다.

2.3 오버라이딩 예제

class Animal {
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    // 오버라이딩: 상위 클래스의 메서드를 재정의
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound(); // Animal makes a sound

        Dog dog = new Dog();
        dog.sound(); // Dog barks

        // 다형성
        Animal myDog = new Dog();
        myDog.sound(); // Dog barks - 오버라이딩된 메서드 호출
    }
}

 

위 예제에서 Dog 클래스는 Animal 클래스의 sound 메서드를 오버라이딩하여 자신만의 동작을 정의합니다. Dog 클래스의 인스턴스는 sound 메서드를 호출할 때, Dog 클래스에 정의된 메서드를 호출하게 됩니다.

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

오버로딩과 오버라이딩은 이름이 비슷하지만, 사용 목적과 방법에서 큰 차이가 있습니다. 이를 명확하게 이해하기 위해 아래 표를 참고하세요.

비교 항목 오버로딩(Overloading) 오버라이딩(Overriding)
정의 동일한 이름의 메서드를 매개변수의 차이에 따라 여러 개 정의하는 것 상위 클래스의 메서드를 하위 클래스에서 재정의하는 것
목적 메서드 이름 재사용으로 코드 가독성 및 유지보수성 향상 다형성을 통해 상위 클래스의 기능을 하위 클래스에서 확장 또는 수정
메서드 시그니처 메서드 이름은 동일하나, 매개변수의 타입, 개수, 순서가 달라야 함 메서드 이름, 매개변수, 리턴 타입이 모두 동일해야 함
컴파일 시점 컴파일 타임에 결정 런타임에 결정
상속 관계 상속 관계가 없어도 사용 가능 반드시 상속 관계여야 함
접근 제어자 메서드마다 자유롭게 설정 가능 상위 클래스의 접근 제어자보다 넓거나 동일해야 함
애너테이션 사용하지 않음 @Override 애너테이션을 사용하여 명시적으로 표시 가능

 

4. 스프링부트에서의 오버로딩과 오버라이딩

스프링부트(Spring Boot)는 Java 애플리케이션 개발을 쉽게 해주는 프레임워크로, 오버로딩과 오버라이딩을 활용하여 효율적인 RESTful API를 구현할 수 있습니다. 아래 예제에서는 오버로딩을 통해 다양한 요청에 대한 처리 메서드를 정의하고, 오버라이딩을 통해 기본 동작을 확장하는 방법을 설명합니다.

4.1 스프링부트 오버로딩 예제

스프링부트에서 오버로딩을 사용하여 여러 요청을 처리하는 컨트롤러 메서드를 정의할 수 있습니다. 예를 들어, 사용자 정보를 요청하는 API에서 오버로딩을 활용하여 다양한 형태의 요청을 처리해 보겠습니다.

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

    // 오버로딩: 전체 사용자 목록 조회
    @GetMapping
    public String getUsers() {
        return "Returning all users";
    }

    // 오버로딩: 특정 사용자 ID로 조회
    @GetMapping("/{userId}")
    public String getUserById(@PathVariable int userId) {
        return "Returning user with ID: " + userId;
    }

    // 오버로딩: 특정 사용자 이름으로 조회
    @GetMapping("/search")
    public String getUserByName(@RequestParam String name) {
        return "Returning user with name: " + name;
    }
}

위 예제에서는 동일한 GET 요청이지만, 요청 경로나 매개변수에 따라 다른 메서드가 호출됩니다. 이를 통해 다양한 요청을 유연하게 처리할 수 있습니다.

4.2 스프링부트 오버라이딩 예제

스프링부트에서는 서비스 계층에서 기본 동작을 오버라이딩하여 커스터마이징할 수 있습니다. 예를 들어, 기본 CRUD 동작을 제공하는 서비스 클래스를 만들고, 이를 확장하여 특정 메서드를 오버라이딩해보겠습니다.

import org.springframework.stereotype.Service;

// 기본 CRUD 서비스
@Service
public class UserService {
    public String createUser(String name) {
        return "User " + name + " created!";
    }

    public String getUser(int id) {
        return "User with ID " + id;
    }
}

// UserService를 확장하여 오버라이딩
@Service
public class CustomUserService extends UserService {

    // 사용자 생성 로직 오버라이딩
    @Override
    public String createUser(String name) {
        // 사용자 이름을 모두 대문자로 변환하여 생성
        return "Custom User " + name.toUpperCase() + " created!";
    }

    // 기본 동작을 유지하면서 특정 조건에 대해 확장
    @Override
    public String getUser(int id) {
        if (id == 1) {
            return "Admin User";
        } else {
            return super.getUser(id); // 상위 클래스의 기본 동작 호출
        }
    }
}

위 예제에서는 CustomUserService가 UserService의 createUser 메서드를 오버라이딩하여 사용자 이름을 대문자로 변환하여 생성하고, getUser 메서드는 특정 ID에 대해 상위 클래스의 메서드를 호출하여 기본 동작을 유지하면서도 추가적인 로직을 제공하도록 합니다.

5. 결론

오버로딩과 오버라이딩은 Java에서 메서드의 이름을 재사용하여 코드의 가독성과 유지보수성을 높이기 위해 사용되는 기능입니다. 오버로딩은 메서드 이름을 동일하게 유지하면서 다양한 매개변수 조합을 처리할 수 있도록 해주고, 오버라이딩은 상위 클래스의 메서드를 하위 클래스에서 재정의하여 다형성을 구현할 수 있게 합니다.

스프링부트에서는 오버로딩을 활용하여 다양한 RESTful API 요청을 처리하고, 오버라이딩을 통해 기본 동작을 확장하고 커스터마이징할 수 있습니다. 이 두 개념을 잘 이해하고 활용하면, Java와 스프링부트에서 보다 유연하고 효율적인 프로그램을 작성할 수 있습니다.

오버로딩과 오버라이딩을 잘 활용하여 객체지향 프로그래밍의 장점을 극대화하고, 스프링부트에서 더 나은 API와 서비스를 만들어보세요!

끝.

728x90
300x250