포스트

Java 객체지향을 다시 정리하며: 오버로딩, 오버라이딩, 다형성

과거 Java 기초 수업 노트 여러 편을 하나로 합쳐 객체지향의 핵심 개념을 다시 정리합니다.

Java 객체지향을 다시 정리하며: 오버로딩, 오버라이딩, 다형성

Tistory의 Java 기초 학습 글 중 오버로딩, 오버라이딩, 객체 형변환 관련 내용을 하나로 통합했다.

Java를 처음 배울 때는 각각의 용어를 따로 외웠다.

  • Overloading
  • Overriding
  • Up Casting
  • Down Casting

하지만 실제로는 이 개념들이 타입, 상속, 다형성이라는 하나의 흐름으로 연결된다.

오버로딩

오버로딩은 같은 클래스 안에서 같은 이름의 메서드를 여러 개 정의하는 것이다.

단, 파라미터 목록이 달라야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Calculator {

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

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

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

메서드 이름은 같지만 호출 시점에 전달한 인자에 따라 어떤 메서드를 사용할지 결정된다.

반환 타입만 다르게 만드는 것은 오버로딩이 아니다.

오버라이딩

오버라이딩은 상위 타입에서 정의한 메서드를 하위 클래스에서 다시 구현하는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Animal {

    public void sound() {
        System.out.println("animal");
    }
}

public class Dog extends Animal {

    @Override
    public void sound() {
        System.out.println("bow-wow");
    }
}

오버라이딩의 핵심은 런타임 다형성과 연결된다는 점이다.

1
2
Animal animal = new Dog();
animal.sound();

변수 타입은 Animal이지만 실제 객체는 Dog이므로 Dog의 sound()가 호출된다.

업캐스팅

하위 타입의 객체를 상위 타입 참조변수로 다루는 것이다.

1
Animal animal = new Dog();

이 방식 덕분에 여러 구현체를 하나의 상위 타입으로 처리할 수 있다.

1
2
3
4
5
6
7
8
List<Animal> animals = List.of(
    new Dog(),
    new Cat()
);

for (Animal animal : animals) {
    animal.sound();
}

호출하는 코드는 Dog와 Cat의 구체 구현을 몰라도 된다.

다운캐스팅

상위 타입 참조를 다시 구체 하위 타입으로 변환하는 것이다.

1
2
3
if (animal instanceof Dog dog) {
    dog.fetch();
}

다운캐스팅이 자주 필요하다면 상위 타입의 추상화가 충분한지 다시 살펴볼 필요가 있다.

호출자가 계속 구체 타입을 확인해야 한다면 다형성의 장점을 제대로 활용하지 못하고 있을 수 있다.

인터페이스와 다형성

실제 Spring 애플리케이션에서는 클래스 상속보다 인터페이스를 통한 다형성을 더 자주 접한다.

1
2
3
public interface PaymentClient {
    PaymentResult pay(PaymentCommand command);
}
1
2
3
4
@Component
public class KakaoPaymentClient implements PaymentClient {
    // ...
}
1
2
3
4
@Component
public class TossPaymentClient implements PaymentClient {
    // ...
}

Service는 구체 구현보다 인터페이스에 의존할 수 있다.

1
2
3
4
5
6
7
8
9
@Service
public class PaymentService {

    private final PaymentClient paymentClient;

    public PaymentService(PaymentClient paymentClient) {
        this.paymentClient = paymentClient;
    }
}

이 지점에서 객체지향 기초와 Spring의 의존성 주입이 연결된다.

예전에는 문법으로 봤고, 지금은 의존성으로 본다

처음에는 다음처럼 기억했다.

  • 오버로딩: 같은 이름, 다른 파라미터
  • 오버라이딩: 상속받은 메서드 재정의
  • 업캐스팅: 자식을 부모 타입으로
  • 다운캐스팅: 부모 타입을 자식 타입으로

지금은 이 개념의 목적을 더 중요하게 본다.

구체 구현을 직접 알고 있는 코드를 줄이고, 공통된 추상화에 의존하도록 만들기 위해 다형성을 사용한다.

문법 자체보다 변화가 생겼을 때 어떤 코드까지 영향을 받는지를 생각하면 객체지향 개념이 실제 설계와 연결된다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.