[강의 Java 다형성(Polymorphism)

Jerry·2025년 7월 14일

Topic

Polymorphism
Abstract Class
Interface
SOLID Principles

What I Learned

다형성(Polymorphism)

개념

  • 하나의 참조 변수로 여러 타입의 객체를 가리킬 수 있는 성질
  • 상속을 이용해 부모 타입으로부터 파생된 여러 가지 타입의 자식 객체를 부모 클래스 타입 하나로 대체할 수 있는 문법을 제공
  • 대표적으로 method overriding, interface, up casting, dynamic binding 등을 통해 구현

클래스 형 변환

Up casting

  • 상속 관계에 있는 부모, 자식 클래스 간에 부모 타입의 참조형 변수가 모든 자식 타입의 객체 주소를 받을 수 있는 개념
  • 자동으로 처리되며 명시적 캐스팅이 필요 없음
  • 단, Type이 부모임으로 부모에서 정의된 필드와 메소드(추상 메소드 포함)만 호출 가능

Down casting

  • 상속 관계에서 부모 클래스 타입으로 선언된 객체를 자식 클래스 타입으로 변환하는 것
  • 컴파일러는 이를 허용하지만, 런타임에서 해당 객체가 하위 클래스의 인스턴스여야 ClassCastException이 발생하지 않는다.

Safe casting - instanceof

  • 객체가 실제로 특정 타입인지 확인한 후에만 casting을 수행하는 방식
  • instanceof 현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용하는 연산자, 클래스 타입이 맞으면 true, 아니면 false를 반환
if (animal instanceof dog) {
	Dog dog = (Dog) animal;
    dog.bark();
}

// Java 16+
if (animal instanceof Dog dog) {
	dog.bark();
}

객체 배열과 다형성

  • 객체 배열에서는 하나의 부모 타입으로 여러 자식 객체를 저장할 수 있습니다.
  • Car[] 배열에 다양한 자동차 객체를 저장하고 자식에서 drive()를 오버라이딩 하는 경우, 호출할 때 실제 객체 타입에 맞는 메소드가 실행됩니다.
  • 이렇게 실행 시점에 메소드가 결정되는 것을 동적 바인딩이라고 합니다.
    → 일반적으로 배열이 아닌 컬렉션을 사용합니다.
Car[] carArr = new Car[5];

carArr[0] = new Sonata();
carArr[1] = new Avante();
carArr[2] = new Grandure();

for (Car car : carArr) {
	car.drive();
}

매개변수와 다형성

  • 매개변수 역시 다형성을 이용하여 메소드를 호출할 때 부모 타입을 활용하여 자식 타입의 객체를 전달받을 수 있습니다.
public void execute() {
	driveCar(new Sonata());
    driveCar(new Avante());
    driveCar(new Grandure());
}

public void driveCar(Car c) {}

동적 바인딩(Dynamic binding)

  • 동적 바인딩은 호출되는 메소드를 프로그램이 실행될 때 결정하는 방식입니다.
  • 부모 타입의 참조 변수가 자식 객체를 참조하고, 메소드가 오버라이딩 되어 있어야 합니다.
  • 정적 바인딩: 컴파일 시점에 실제 실행할 메소드 코드와 호출하는 코드를 연결하는 것
public Class Car {
	public void run() {
    	System.out.println("Car Run");
    }
}

public class Sonata extends Car {
	@Override
    public void run() {
    	System.out.println("Sonata Run");
    }
}

public class Main {
	public static void driveCar(Car c) {
    	c.run(); // 이 부분이 동적 바인딩에 해당
    }
    
    public static void main(String[] args) {
    	driveCar(new Sonata()); // Sonata의 run() 호출
    }
}

추상 클래스(Abstract class)

개념

  • 여러 클래스에서 공통으로 사용할 수 있는 기능의 뼈대를 제공하는 설계 기법
  • 추상 클래스는 추상 메소드와 일반 메소드를 가질 수 있습니다.
  • 추상 메소드는 자식 클래스에서 반드시 오버라이딩을 구현해야 합니다. (오버라이딩하지 않는 경우 컴파일 에러 발생)
  • 최종적으로 상속을 통한 코드 재사용과 공통 로직 제공이 목적
[Access modifier] abstract class ClassName {
	// 추상 메소드
	[Access modifier] abstract [ReturnType] MethodName([Parameters]);
    
    // 일반 메소드
    public void method() {}
}

인터페이스(Interface)

개념

  • 인터페이스(interface)는 클래스가 구현해야 할 기능의 목록을 정의한 일종의 설계서입니다.
  • 인터페이스는 추상 메소드와 일반 메소드를 가질 수 있으나 멤버 변수는 선언할 수 없고 상수만 선언할 수 있습니다. (public static final로 자동 선언됨)
[Access modifier] interface InterfaceName {
	// 상수(암묵적으로 public static final)
    int CONSTANT_VALUE = 0;
    
    // 추상 메소드(암묵적으로 public abstract)
    void abstractMethod();


	// Java8+

	// default 메소드(공통적으로 사용할 메소드, 오버라이딩 가능)
    default void defaultMethod() {}

    // private 메소드(인터페이스 내부에서만 사용할 메소드, 오버라이딩 불가능)
    private void privateMethod() {}

    // static 메소드(정적 메소드, 오버라이딩 불가능)
    static void staticMethod() {}
}

다중 상속

  • 클래스는 단일 상속만 가능하지만 인터페이스는 여러 개를 동시에 구현(implements)할 수 있습니다.
  • default method의 이름이 겹칠 경우 클래스에서 오버라이딩하지 않으면 컴파일 에러가 발생합니다.

장점

  • 다중 상속이 가능하여 유연한 설계가 가능합니다.
  • 기능별로 인터페이스를 분리하면 모듈화와 재사용성이 높아집니다.
  • 클래스 간 결합도를 낮춰 유지보수와 확장에 유리합니다.
  • 다양한 클래스에서 동일한 인터페이스를 구현하여 다형성을 실현할 수 있습니다.

SOLID 원칙

  • 객체지향 설계 원칙(OOP Design Principles)은 객체지향 프로그래밍의 설계 품질을 향상하기 위한 기법
  • SOLID 원칙(SRP, OCP, LSP, ISP, DIP)은 클래스와 객체의 책임, 의존성, 역할을 명확히 분리하여 유연한 구조를 만들 수 있습니다.
    • S - 단일 책임 원칙(Single Responsibility Principle): 클래스는 하나의 책임만 가져야 합니다.(하나의 변화 이유만 가져야 합니다.)
    • O - 개방/폐쇄 원칙(Open/Closed Principle): 확장에는 열려 있고, 변경에는 닫혀 있어야 합니다.(새 기능은 기존 코드를 수정하지 않고 확장해야 합니다.)
    • L - 리스코프 치환 원칙(Liskov Substitution Principle): 자식 클래스는 부모 클래스를 대체할 수 있어야 합니다.(부모 객체를 사용하는 로직이 자식으로 바뀌어도 문제없어야 합니다.)
    • I - 인터페이스 분리 원칙(Interface Segregation Principle): 클라이언트는 자신이 사용하지 않는 메소드에 의존하면 안됩니다.(인터페이스는 작고 명확하게 분리해야 합니다.)
    • D - 의존 역전 원칙(Dependency Inversion Principle): 상위 모듈은 하위 모듈에 의존하면 안 되고, 둘 다 추상화에 의존해야 합니다.(구체화가 아닌 인터페이스(추상)에 의존해야 합니다.)

To study

profile
Backend engineer

0개의 댓글