티스토리 뷰

다트 공식 문서 번역

다트] 생성자

철철박사 2024. 8. 1. 22:54
반응형

생성자는 클래스의 인스턴스를 생성하는 특별한 함수이다. 다트는 여러 종류의 생성자를 구현할 수 있다. 디폴트 생성자를 제외하고는 

  • 일반 생성자 : 새로운 인스턴스를 생성하고 인스턴스 변수를 초기화한다. 일반 생성자는 하나만 정의할 수 있다.
  • 디폴트 생성자 : 생성자가 정의되어 있지 않으면 자동으로 만들어지는 생성자이다. 인자를 받지 않으며 이름이 없다.
  • 명명된 생성자 : 생성자의 목적을 명확히 할 수 있다. 명명된 생성자는 여러 개를 정의할 수 있다.
  • 상수 생성자 : 컴파일 시 상수로 인스턴스를 생성할 수 있게 한다.
  • 팩토리 생성자 : 하위 타입의 새 인스턴스를 생성하거나 캐시에서 기존 인스턴스를 반환할 수 있다.
  • 재지정 생성자 : 동일한 클래스의 다른 생성자로 호출을 전달한다.

 

 

 

생성자의 종류

일반 생성자

일반 생성자를 사용하여 클래스를 인스턴스화할 수 있다. 일반 생성자의 이름은 클래스 이름과 같아야 한다. 반환 타입은 없으며 선택적으로 매개변수를 추가할 수 있다.

class Point {
  // 인스턴스 변수 선언
  double x = 2.0;
  double y = 2.0;

  // 
  Point(this.x, this.y);
}

 

 

디폴트 생성자

생성자를 선언하지 않으면 다트는 디폴트 생성자를 생성한다. 디폴트 생성자는 인수가 없는 일반 생성자가 된다.

 

 

명명된 생성자

클래스는 일반 생성자를 하나만 정의할 수 있다. 하지만 명명된 생성자를 사용하면 여러 생성자를 추가로 정의할 수 있다. 명명된 생성자는 추가로 이름이 포함되기 때문에 명확성을 제공한다.

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  // 일반 생성자
  Point(this.x, this.y);

  // 명명된 생성자
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

 

하위 클래스는 상위 클래스의 명명된 생성자를 상속하지 않는다. 하위 클래스에서 상위 클래스에 정의된 명명된 생성자가 필요하다면, 하위 클래스에 직접 구현해야 한다.

 

 

상수 생성자

상수 생성자는 객체를 컴파일 타임 상수로 만들 수 있게 한다. 상수 생성자를 정의하기 위해서는 모든 인스턴스 변수를 final로 설정해야 하고 클래스 정적 변수는 static const가 되어야 한다. 상수 생성자는 본문이 없다.

class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

 

상수 생성자는 항상 상수를 만드는 것이 아니다. 객체 생성 시 상수 콘텍스트인 경우에만 상수가 된다.

// point1은 상수가 된다.
const point1 = const Point();

// point2는 상수 컨텍스트이므로 상수가 된다. 
const point2 = const Point();

// Point3는 변수이지만 상수 Point를 담고 있다.
var point3 = const Point();

// Point4는 상수가 아닌 일반 객체가 된다.
var Point4 = Point();

 

 

재지정 생성자

생성자가 동일한 클래스의 다른 생성자를 호출할 수 있다. 재지정 생성자는 본문이 없다. 본문 대신 콜론(:) 뒤에 this를 사용해서 지정할 생성자를 호출한다.

class Point {
  double x, y;

  Point(this.x, this.y);

  // 재지정 생성자
  Point.alongXAxis(double x) : this(x, 0);
  Point.origin() : this.alongXAxis(0);
}

 

 

팩토리 생성자

다음 경우 중 하나에 속한다면 팩토리 생성자를 구현하자.

  • 생성자가 항상 클래스의 새 인스턴스를 생성하지 않는 경우
  • 하위 타입의 새 인스턴스를 생성하는 경우
  • 인스턴스를 구성하기 전에 비정상적인 작업을 수행해야 하는 경우, 인수 확인이나 초기화 목록에서 처리할 수 없는 기타 처리가 포함된다.

팩토리 생성자는 this에 접근할 수 없다.

 

다음 예제네는 두 개의 팩토리 생성자를 포함한다. Logger 팩토리 생성자는 캐시에서 객체를 반환한다. Logger.fromJson 팩토리 생성자는 JSON 객체에서 최종 변수를 초기화한다.

class Logger {
  final String name;
  bool mute = false;

  // _cache는 라이브러리 전용으로, 이름 앞에 _가 붙어 있습니다.
  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);
  }
}

 

팩토리 생성자는 다른 생성자처럼 동일하게 사용하면 된다.

var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);

 

 

재지정 팩토리 생성자

재지정 팩토리 생성자는 호출 시 다른 클래스의 생성자가 호출되도록 지정한다. 

class Circle implements Shape {
  final double radius;

  const Circle(this.radius);
}

class Shape {
  factory Shape.cicle(double radius) = Circle;
}

 

 

재지정 팩토리는 일반 팩토리와 달리 다음과 같은 이점이 있다.

  • 추상 클래스는 다른 클래스의 상수 생성자를 사용하는 상수 생성자를 제공할 수 있다.
  • 재지정 팩토리 생성자는 타입 매개변수와 기본 값을 반복할 필요가 없다.

다음은 추상 클래스에서 다른 클래스의 상수 생성자를 사용하는 예제 코드이다.

abstract class Shape {
  const Shape();
  
  const factory Shape.circle(double radius) {
    return const Circle(radius); // 에러 : raduis가 상수가 아님
  }

  const factory Shape.circle(double radius) = Circle;  // OK
}

class Circle extends Shape {
  final double radius;

  const Circle(this.radius) : super();
}

 

 

생성자 Tear-Offs

다트는 생성자를 직접 호출하지 않고, 생성자를 매개변수로 전달할 수 있다. 이를 tear-off라고 하며, 생성자를 동일한 매개변수로 호출하는 클로저 역할을 한다.

 

생성자 tear-off는 메서드가 수용하는 동일한 매개변수와 반환 타입을 갖는 경우 전달할 수 있다. 

 

tear-off는 익명함수와는 다르다. 익명함수는 생성자에 대한 래퍼 역할을 하지만, tear-off는 생성자 그 자체이다.

좋은 예 - 생성자 tear-off를 사용하는 경우
var strings = charCodes.map(String.fromCharCode);
var buffers = charCodes.map(StringBuffer.new);

나쁜 예 - 람다로 생성자를 래퍼하는 경우
var strings = charCodes.map((code) => String.fromCharCode(code));
var buffers = charCodes.map((code) => StringBuffer(code));

 

 

 

인스턴스 변수 초기화

다트에서는 세 가지 방법으로 변수를 초기화할 수 있다.

 

변수 선언 시 인스턴스 변수 초기화

변수를 선언할 때 인스턴스 변수를 초기화할 수 있다.

class PointA {
  double x = 1.0;
  double y = 2.0;

  @override
  String toString() {
    return 'PointA($x,$y)';
  }
}

 

 

초기화 형식 매개변수 사용

생성자 인수를 인스턴스 변수에 할당하는 패턴을 단순화하기 위해 다트는 초기화 형식 매개변수를 제공한다. 생성자 선언에서 this.<인스턴스 변수 이름>을 사용하고 본문을 생략한다. 

 

초기화 형식 매개변수는 널-불가능 변수나 final 인스턴스 변수를 초기화하는 데에도 사용할 수 있다.

class Point {
  int x;
  final int y;
  
  Point(int x, int y) {
   this.x = x;  // 에러
   this.y = y;  // 에러
  }
}

 

초기화 형식 매개변수를 사용하면 에러가 발생하지 않게 된다.

class PointB {
  int x;
  final int y;

  // 초기화 형식 매개변수로 x와 y 인스턴스 변수를 설정합니다.
  PointB(this.x, this.y);

  // 초기화 형식 매개변수는 옵셔널 매개변수에서도 사용할 수 있습니다.
  PointB.optional([this.x = 0, this.y = 0]);
}

 

private 필드는 명명된 매개변수에서 초기화 형식 매개변수를 사용할 수 없다. 대신 다음과 같이 초기화 목록을 사용할 수 있다.

class PointB {
  // ...
  
  PointB.namedPrivate({required double x, required double y})
      : _x = x,
        _y = y;
        
  // ...
}

 

private 필드가 아니라면 명명된 매개변수와 함께 사용할 수 있다.

class PointC {
  double x;
  double y;

  // 초기화 형식 매개변수로 기본값이 있는 생성자
  PointC.named({this.x = 1.0, this.y = 1.0});

  @override
  String toString() {
    return 'PointC.named($x,$y)';
  }
}

// 명명된 변수를 사용하는 생성자.
final pointC = PointC.named(x: 2.0, y: 2.0);

 

생성자 매개변수는 널-가능 필드에도 사용할 수 있다.

class PointD {
  double? x;
  double? y;

  // 초기화 형식 매개변수를 사용하는 일반 생성자
  PointD(this.x, this.y);

  @override
  String toString() {
    return 'PointD($x,$y)';
  }
}

 

 

초기화 목록 사용

초기화 목록을 사용하면 생성자 본문이 실행되기 전에 인스턴스 변수를 초기화할 수 있다. 초기화 목록의 항목은 쉼표로 구분된다.

// 초기화 목록은 생성자 본문이 실행되기 전에 인스턴스 변수를 설정합니다.
Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');
}

 

초기화 목록의 오른쪽은 this에 접근할 수 없다.

Point.raiseError() : x = this.y;  // 초기화 목록에서 this에 접근해서 에러 발생

 

개발 중에 입력 값을 검증하려면 초기화 목록에서 assert를 사용할 수도 있다.

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

 

 

초기화 목록은 final 필드를 설정하는 데 도움이 된다. 

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);
}

 

 

 

생성자 상속

서브 클래스(자식 클래스)는 슈퍼 클래스(부모 클래스)의 생성자를 상속하지 않는다. 클래스가 생성자를 선언하지 않으면 자동으로 만들어지는 디폴트 생성자를 사용할 수 있다.

 

서브 클래스의 생성자는 슈퍼 클래스의 생성자를 호출할 수 있다. 만약 슈퍼 클래스에 디폴트 생성자를 호출한다면, 호출을 생략할 수도 있다.

 

비-디폴트 슈퍼 클래스 생성자

다트 생성자는 다음 순서로 실행된다.

  • 초기화 목록
  • 슈퍼 클래스 생성자
  • 해당 클래스의 생성자

 

다음은 슈퍼 클래스의 생성자를 호출하는 예제이다. 생성자 본문 전에 콜론(:) 뒤에 슈퍼 클래스의 생성자를 호출하면 된다.

class SuperClass {
  int x;
  SuperClass(this.x);
  SuperClass.zero() : x = 0;
}

class SubClass extends SuperClass {
  SubClass() : super(0);
  SubClass.zero() : super.zero();
}

 

슈퍼 클래스의 생성자 인수는 this에 접근할 수 없다. 인수는 정적 메서드를 호출할 수 있지만 인스턴스 메서드는 호출할 수 없다.

 

 

슈퍼 매개변수

슈퍼 클래스의 생성자를 호출시 각 매개변수를 전달하는 대신, 슈퍼 초기화 매개변수를 사용하여 인수를 전달할 수 있다. 슈퍼 매개변수는 초기화 형식 매개변수와 유사한 문법과 의미를 가진다.

class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // 기본 슈퍼 생성자에 x와 y 매개변수를 전달합니다.
  // Vector3d(double x, double y, this.z) : super(x, y);
  Vector3d(super.x, super.y, this.z);
}

 

위치 인수를 가지는 슈퍼 생성자를 호출 시에는 슈퍼 초기화 매개변수는 위치 인수가 될 수 없다.

class Vector3d extends Vector2d {
  final double z;

  // 슈퍼 생성자(`super(0)`)를 위치 인수로 호출하는 경우,
  // 슈퍼 파라미터(`super.x`)를 사용하는 것은 오류를 초래합니다.
  Vector3d.xAxisError(super.x): z = 0, super(0); // 에러
}

 

슈퍼 생성자가 명명된 인수를 갖는 경우에는 슈처 초기화 매개변수를 사용할 수 있다.

class Vector2d {
  // ...
  Vector2d.named({required this.x, required this.y});
}

class Vector3d extends Vector2d {
  final double z;

  // y 매개변수를 명명된 슈퍼 생성자로 전달합니다.
  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}
반응형

'다트 공식 문서 번역' 카테고리의 다른 글

다트] 클래스를 확장하기  (0) 2024.08.03
다트] 메서드  (0) 2024.08.01
다트] 클래스  (0) 2024.07.29
다트] 에러 처리하기  (0) 2024.07.29
다트] 분기  (0) 2024.07.28
댓글
공지사항