생성자
- 클래스와 같은 이름의 함수를 생성하면 생성자가 된다.
- (선택적으로) 이름을 부여하면 명명된 생성자 (Named constructors) 를 만들 수 있다.
- 클래스의 인스턴스를 생성하기 위한 일반 생성자를 generative constructor 라 한다.
class Point {
double x = 0;
double y = 0;
Point(double x, double y) {
// 사실 인스턴스 변수를 초기화하는 더 좋은 방법이 있다.
this.x = x;
this.y = y;
}
}
Dart 에서
this
는 이름 충돌(name conflict)이 발생할 때만 사용하는 것이 좋다. 다트 스타일은this
를 생략한다.
공식 매개변수 초기화하기 (initializing formal parameters)
- 생성자의 인자를 인스턴스 변수에 할당하는 패턴은 매우 일반적이다.
- 다트는 initializing formal parameter 를 통해 이를 쉽게 초기화하도록 도와준다.
- null 이 될 수 없는 인스턴스 변수 (non-nullable instance variable) 혹은
final
인스턴스 변수에도 사용할 수 있다.- 위 두가지 경우는 반드시 초기화 되어야 하는 인스턴스 변수의 예이다.
class Point {
final double x;
final double y;
// 생성자의 바디 부분이 동작하기 전에 x, y 인스턴스 변수가 할당(set)된다.
Point(this.x, this.y);
}
기본 생성자
- 아무 생성자도 정의하지 않았을 때 생성된다.
- 상위 클래스에서 아무런 인자가 없는 생성자를 호출한다.
생성자는 상속되지 않는다
- 하위 클래스는 상위 클래스의 생성자를 상속받지 않는다.
- 아무런 생성자를 정의하지 않은 경우 기본 생성자만 가진다.
명명된 생성자 (Named constructors)
- 더 명시적인 여러 개의 생성자를 가진 클래스를 만들 때 유용하다.
- 생성자는 상속되지 않는 점을 다시 한번 상기하자. 이는 명명된 생성자도 마찬가지이다.
- [[2.1 item1. 생성자 대신 정적 팩터리 메서드를 고려하라]]
- 자바의 정적 팩터리 메서드를 언어 차원에서 지원해주는 느낌이다.
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
Point(this.x, this.y);
// Named constructor
Point.origin()
: x = xOrigin,
y = yOrigin;
}
기본 생성자가 아닌 상위 클래스의 생성자 호출하기
- 하위 클래스의 생성자는 기본적으로 슈퍼 클래스의 기본 생성자를 호출한다.
- 생성자 바디의 시작 부분에서 호출된다.
- 이니셜라이저 리스트도 사용 중이면, 슈퍼클래스가 호출되기 전에 실행된다.
실행 순서는 아래와 같다.
- 이니셜라이저 리스트
- 슈퍼 클래스의 인자 없는 생성자
- 메인 클래스의 인자 없는 생성자
- 슈퍼 클래스에 이름 없고 인자 없는 생성자가 없다면, 슈퍼 클래스에서 생성자 하나를 선택해 직접 호출해야 한다.
- 생성자 바디 바로 전에 있는
:
이후에 슈퍼 클래스의 생성자를 명시해줘야 한다.
class Person {
String? firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person 클래스는 기본 생성자가 없기 때문에 `:` 이후에 어떤 생성자를 사용할지 명시해야 한다.
Employee.fromJson(super.data) : super.fromJson() {
print('in Employee');
}
}
void main() {
var employee = Employee.fromJson({});
print(employee);
}
- 슈퍼클래스 생성자에 대한 인수는 생성자를 호출하기 전에 평가되기 때문에 인수는 함수 호출 같은 표현식이 될 수 있다.
class Employee extends Person {
Employee() : super.fromJson(fetchDefaultData());
}
슈퍼 클래스 생성자의 인자는
this
에 접근할 수 없다.static methods
는 호출할 수 있어도 인스턴스 메서드를 호출할 수는 없다. (아직 해당 인스턴스가 만들어지기 전이기 때문일 것 같다.)
이니셜라이저 리스트
- 생성자 바디가 실행되기 전에 인스턴스 변수를 초기화할 수 있다.
- 이니셜라이저는 쉼표로 구분한다.
final
로 된 필드를 설정하는데 매우 유용하다.
// 이니셜라이저 리스트는 생성자 바디가 실행되기 전에 인스턴스 변수 값을 설정한다.
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}
이니셜라이저의 우측에서는
this
에 접근할 수 없다.
import 'dart:math';
class Point {
final double x;
final double y;
final double distanceFromOrigin;
Point(double x, double y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
void main() {
var p = Point(2, 3);
print(p.distanceFromOrigin);
}
- 개발 시 이니셜라이저 리스트에
assert
를 통해 값을 검증할 수 있다.
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
Super parameters
- 생성자의 슈퍼 호출에 각 매개변수를 직접 전달하지 않으려면 슈퍼 이니셜라이저 매개변수를 사용하여 매개변수를 지정된 혹은 기본 슈퍼클래스 생성자로 전달할 수 있다.
- 이 기능은 리디렉션 생성자와 함께 사용할 수 없다.
- 슈퍼 이니셜라이저 매개변수는 formal parameters 를 초기화하는 것과 문법적으로 의미적으로 유사합니다.
class Vector2d {
final double x;
final double y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// x, y 를 다음과 같이 슈퍼 생성자로 전달한다.
// Vector3d(final double x, final double y, this.z) : super(x, y);
Vector3d(super.x, super.y, this.z);
}
- 상위 생성자 호출에 이미 위치가 있는 인수가 있다면, 슈퍼 이니셜라이저 매개변수 (super-initializer parameters) 는 위치가 있을 수 없다.
- [[001.다트-언어-마스터하기#순서가 고정된 매개변수와 이름이 있는 매개변수]]
- 하지만 항상 이름을 지정할 수 있다.
class Vector2d {
// ...
Vector2d.named({required this.x, required this.y});
}
class Vector3d extends Vector2d {
// ...
// Forward the y parameter to the named super constructor like:
// Vector3d.yzPlane({required double y, required this.z})
// : super.named(x: 0, y: y);
Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}
생성자 리다이렉팅하기
- 때때로 생성자의 목적은 같은 클래스의 다른 생성자로 리다이렉팅하는 것이다.
- 리다이렉팅 하는 생성자의 바디는 비어있고
:
뒤에 생성자 호출이 표현된다.
class Point {
double x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(double x) : this(x, 0);
}
상수 생성자 (Constant constructors)
- 클래스가 생성한 오브젝트가 변하지 않는다면, 오브젝트를 컴파일 타임 상수로 만들 수 있다.
const
생성자를 정의하고 모든 인스턴스 변수가final
인지 확인해야 한다.- 상수 생성자가 항상 상수만 생성하진 않으므로 유의해야 한다.
const
키워드가 중요하다. [[001.다트-클래스#상수 생성자]]
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
팩토리 생성자 (Factory constructors)
- 항상 새로운 인스턴스를 생성하지 않는 생성자를 구현할 때,
factory
라는 키워드를 사용한다. - 캐시에서 인스턴스를 가져오거나 하위 타입의 인스턴스를 반환할 수도 있다.
- 이니셜라이저 리스트에서 처리할 수 없는 로직을 사용하여
final
변수를 초기화할 때도 사용한다.- [[004.생성자(constructors)#이니셜라이저 리스트]]
final
변수의 늦은 초기화를 다루고 싶다면 use late final 을 '조심스럽게' 참고해보는 것도 좋다.
- 아래는
Logger
를 캐시에서 꺼내오는 코드의 예제이다. Logger.fromJson
팩토리는final
변수를 JSON 오브젝트로부터 초기화한다.
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
factory Logger.fromJson(Map<String, Object> json) {
return Logger(json['name'].toString());
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
factory
생성자는this
에 접근할 수 없다.
- factory 생성자를 다른 생성자처럼 호출할 수 있다.
var logger = Logger('UI');
logger.log('Button clicked');
var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);
레퍼런스
'Dart' 카테고리의 다른 글
다트의 비동기 지원 (Asynchronous Support) (0) | 2023.10.22 |
---|---|
다트 비동기 프로그래밍 (Future, Async, Await) (0) | 2023.10.22 |
다트(Dart) 언어의 클래스(Class) 정리 (0) | 2023.10.12 |
다트(Dart) 언어의 클래스 제어자 (Class modifier) 정리 (0) | 2023.10.12 |
다트(Dart) 언어의 확장 메서드 (Extension methods) 정리 (0) | 2023.10.10 |