배운것들을 정리합니다.
三昧境

Flutter/Dart Language

[플러터 Flutter] extends 와 implements

ujo_orr 2024. 10. 15. 17:31

 

extends (상속)

 

extends를 사용하면 한 클래스가 다른 클래스의 특성(필드와 메서드)상속받습니다.

즉, 부모 클래스의 모든 필드와 메서드를 그대로 물려받아 사용할 수 있고,
필요한 경우 오버라이딩(@override)을 통해 수정할 수 있습니다.

 

  • 특징
    • 기본 구현을 물려받음
      • 자식 클래스는 부모 클래스의 필드나 메서드를 자동으로 상속받으며, 필요한 부분만 재정의할 수 있습니다.
    • 단일 상속
      • Dart에서 한 클래스는 오직 하나의 클래스만 extends로 상속받을 수 있습니다.
    • 상속 관계는 is-a 관계를 형성합니다. 즉, Circle은 Rectangle.

 

abstract class Rectangle {
  int cx = 0, cy = 0; // 필드
  void draw(); // 추상 메서드
}

class Circle extends Rectangle {
  late int radius;

  @override
  void draw() {
    print("> Circle.draw(): center($cx,$cy) with r[$radius]");
  }

  Circle([int givenRadius = 1]) : radius = givenRadius;
}

Circle 클래스는 Rectangle모든 필드(cx, cy)를 상속받으며, draw() 메서드를 재정의(@override)하여 자신의 방식대로 구현합니다.

 


 

implements (인터페이스 구현)

 

implements를 사용하면 한 클래스가 다른 클래스 또는 인터페이스에 명시된 메서드들을 반드시 구현해야 합니다.

그러나, 상속과 달리 부모 클래스의 필드나 메서드의 구현은 상속받지 않습니다.
모든 메서드와 필드를 새롭게 정의해야 합니다.

 

  • 특징
    • 구현 강제
      • implements를 사용하면 상위 클래스나 인터페이스의 모든 메서드를 강제로 구현해야 합니다.
  • 여러 인터페이스 구현 가능
    • 한 클래스가 여러 클래스를 implements로 구현할 수 있습니다.
  • 상속 관계는 has-a 관계로 볼 수 있으며, 주로 행동 계약으로 사용됩니다. 즉, Circle은 Rectangle처럼 행동할 수 있다는 의미입니다.
  • 필드 또한 새로 정의해야 합니다.

예시코드

abstract class Rectangle {
  int cx = 0, cy = 0; // 필드
  void draw(); // 추상 메서드
}

class Circle implements Rectangle {
  @override
  int cx = 0, cy = 0; // 필드 정의

  late int radius;

  @override
  void draw() {
    print("> Circle.draw(): center($cx,$cy) with r[$radius]");
  }

  Circle([int givenRadius = 1]) : radius = givenRadius;
}

Circle 클래스는 Rectangle 인터페이스를 구현합니다.
필드인 cxcy도 명시적으로 다시 정의해야 하고, draw() 메서드도 새롭게 구현해야 합니다.

 

주요 차이점은

  • 상속 (extends)
    • 부모 클래스의 구현 내용까지 상속받으며, 필요한 부분만 재정의하면 됩니다.
    • 마치 부모로부터 유산을 물려받는 것과 같습니다. 이미 모든 자산을 상속받았고, 그 자산을 직접 사용하는 것과 비슷합니다.
  • 구현 (implements)
    • 부모 클래스의 필드와 메서드는 구현하지 않으며, 모든 것을 직접 새롭게 정의해야 합니다.
    • 마치 계약을 체결하는 것과 같습니다. 계약에 따라 모든 규칙(메서드)을 직접 구현해야 하며, 상속처럼 구현이 제공되지 않습니다.

 

extends는 기본 동작을 상속하고 약간의 변형만 할 때 사용하고,
implements는 계약을 지키면서 완전히 새로운 동작을 구현할 때 사용합니다.

여기서 의문은

abstract와 extends 구문을 함께 사용할 수 있다면
굳이 implements 문법을 사용할 이유가 없다 생각이 되겠지만,
base클래스가 추상 클래스라는 점을 명확하게 이해하고 개발한다는 점에서
추후 발생 가능한 오류들을 줄일 수 있습니다.
extends 문법이 편해 보일수 있지만, 추상 클래스를 사용하는 경우는 가급적
implements 문법을 사용하도록 권장하고 있습니다.

- 출처 풀스택 개발이 쉬워지는 다트&플러터 저자 이성원

 

여기서 더하여 재정의인 @override문을 생략하여도(주석처리, 또는 지워도) 오류없이 잘 구동되는것을 확인할 수 있는데,

컴파일러는 @override가 없어도 메서드 재정의를 인식합니다.
하지만 코드의 가독성이 떨어지고, 실수로 메서드의 시그니처를 변경했을 때 오류를 늦게 발견할 수 있습니다.
@override는 선택적인 애노테이션이지만, 코드의 품질을 높이고 유지보수를 용이하게 만들기 위해 사용하는 것이 좋습니다.
특히 대규모 프로젝트나 여러 개발자가 참여하는 프로젝트에서는 더욱 중요하기때문에 생략을하는것 보단 일일이 작성해주는게 좋습니다.