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 인터페이스를 구현합니다.
필드인 cx와 cy도 명시적으로 다시 정의해야 하고, draw() 메서드도 새롭게 구현해야 합니다.
주요 차이점은
- 상속 (extends)
- 부모 클래스의 구현 내용까지 상속받으며, 필요한 부분만 재정의하면 됩니다.
- 마치 부모로부터 유산을 물려받는 것과 같습니다. 이미 모든 자산을 상속받았고, 그 자산을 직접 사용하는 것과 비슷합니다.
- 구현 (implements)
- 부모 클래스의 필드와 메서드는 구현하지 않으며, 모든 것을 직접 새롭게 정의해야 합니다.
- 마치 계약을 체결하는 것과 같습니다. 계약에 따라 모든 규칙(메서드)을 직접 구현해야 하며, 상속처럼 구현이 제공되지 않습니다.
extends는 기본 동작을 상속하고 약간의 변형만 할 때 사용하고,
implements는 계약을 지키면서 완전히 새로운 동작을 구현할 때 사용합니다.
여기서 의문은
abstract와 extends 구문을 함께 사용할 수 있다면
굳이 implements 문법을 사용할 이유가 없다 생각이 되겠지만,
base클래스가 추상 클래스라는 점을 명확하게 이해하고 개발한다는 점에서
추후 발생 가능한 오류들을 줄일 수 있습니다.
extends 문법이 편해 보일수 있지만, 추상 클래스를 사용하는 경우는 가급적
implements 문법을 사용하도록 권장하고 있습니다.
- 출처 풀스택 개발이 쉬워지는 다트&플러터 저자 이성원
여기서 더하여 재정의인 @override문을 생략하여도(주석처리, 또는 지워도) 오류없이 잘 구동되는것을 확인할 수 있는데,
컴파일러는 @override가 없어도 메서드 재정의를 인식합니다.
하지만 코드의 가독성이 떨어지고, 실수로 메서드의 시그니처를 변경했을 때 오류를 늦게 발견할 수 있습니다.
@override는 선택적인 애노테이션이지만, 코드의 품질을 높이고 유지보수를 용이하게 만들기 위해 사용하는 것이 좋습니다.
특히 대규모 프로젝트나 여러 개발자가 참여하는 프로젝트에서는 더욱 중요하기때문에 생략을하는것 보단 일일이 작성해주는게 좋습니다.
'Flutter > Dart Language' 카테고리의 다른 글
[플러터 Flutter] primarySwatch와 primaryColor는 무슨 차이일까? (0) | 2024.09.13 |
---|---|
[플러터 Flutter] call 메서드란? (0) | 2024.09.13 |
[플러터 Flutter] 조건문의 종류 (0) | 2024.08.01 |
[플러터 Flutter] enum이란? (0) | 2024.07.31 |
[플러터 flutter] initState와 dispose란? (0) | 2024.07.31 |