반응형

Dart, 클래스 class

 

글, 수알치 오상문 

 

다트는 클래스와 상속이 가능한 객체 지향 언어입니다. 모든 객체는 클래스의 인스턴스이며 Null을 제외한 모든 클래스는 Object의 자손입니다(Object). 모든 클래스(최상위 클래스 Object 제외)에 하나의 수퍼 클래스가 있지만 클래스 몸체는 여러 클래스 계층 구조에서 재사용 될 수 있습니다. 또한 상위 클래스 존재하지 않는 기능을 추가할 수 있습니다. 

 

클래스 멤버 사용

 

객체에는 함수와 데이터(각 메서드 및 인스턴스 변수)로 구성된 멤버가 있습니다. 메소드를 호출하면 객체에서 호출합니다. 메소드는 해당 객체의 함수와 자료에 접근할 수 있는데 점(.) 표기를 이용합니다.

 

var p = Point(2, 2); 

assert(p.y == 2);    

double distance = p.distanceTo(Point(4, 4));

 

null 값인 경우를 피하려며 ?. 를 사용합니다. :

 

var a = p?.y;

 

생성자 사용

 

생성자를 사용하여 객체를 만들 수 있습니다. 예제처럼, 생성자 이름은 ClassName 또는 ClassName.identifier일 수 있습니다. 다음 코드는 Point () 및 Point.fromJson () 생성자를 사용하여 Point 객체를 만듭니다.

 

var p1 = Point(2, 2);

var p2 = Point.fromJson({'x': 1, 'y': 2});

 

다음 코드는 동일한 효과가 있지만 생성자 이름 앞에 new 키워드를 사용(옵션)한 경우입니다.

 

var p1 = new Point(2, 2);

var p2 = new Point.fromJson({'x': 1, 'y': 2});

 

버전 참고: 새 키워드는 Dart 2에서 선택사항임

 

일부 클래스는 상수 생성자를 제공합니다. 상수 생성자를 사용하여 컴파일 시간 상수를 만들려면 생성자 이름 앞에 const 키워드를 추가합니다.

 

var p = const ImmutablePoint(2, 2);

 

두 개의 동일한 컴파일 타임 상수를 생성하면 단일 표준 인스턴스가 생성됩니다.

 

var a = const ImmutablePoint(1, 1);

var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // 같은 인스턴스

 

상수형 값에서 생성자 또는 리터럴 앞에 const를 생략할 수 있습니다. 예를 들어, const 맵을 만든 다음 코드를 보기를 살펴보기 바랍니다.

 

// 여러 const가 보입니다. 

const pointAndLine = const { 'point': const [const ImmutablePoint(0, 0)],

                                      'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)], };

 

const 키워드의 첫째 사용을 제외하고 모두 생략할 수 있습니다.

 

const pointAndLine = { 'point': [ImmutablePoint(0, 0)], 'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };

 

상수 생성자가 상수 컨텍스트 외부에 있고 const 없이 호출되면 상수가 아닌 객체를 만듭니다.

 

var a = const ImmutablePoint(1, 1); // 상수

var b = ImmutablePoint(1, 1); // 상수 아님

assert(!identical(a, b)); // 같은 실체가 아님

 

버전 참고: const 키워드는 Dart 2의 상수 컨텍스트에서 선택사항입니다.

 

Dart, 객체 형 검사

 

실행 시간에 객체 유형을 검사하려면 Type 객체를 반환하는 Object 속성 runtimeType을 사용할 수 있습니다.

 

var a = "Korea";

print('자료 형은 ${a.runtimeType}입니다.');

 

[실행결과]

자료형은 String입니다.

 

인스턴스(실체, Instance) 변수

인스턴스 변수는 클래스 자료형을 이용하여 실제로 만들어지는 객체 변수입니다. 

 

class Point {

  double? x;  // 인스턴스 변수 x 선언. 초기 값은 null

  double? y;  // 인스턴스 변수 y 선언. 초기 값은 null.

  double z = 0; // 인스턴스 변수 z 선어. 초기 값은 0

}

 

초기화되지 않은 인스턴스 변수는 null 값을 갖습니다. 

 

모든 인스턴스 변수는 getter 메서드를 생성합니다. 초기화가 없는 최종 인스턴스 변수와 최종 인스턴스 변수도 setter 메서드를 생성합니다. 참조: Getters and setters.

 

다음은 초기화 관련 예제입니다.

 

class Point {

  double? x; // 인스턴스 변수 선언. null 초기화.

  double? y; // 인스턴스 변수 선언. null 초기화.

}

 

void main() {

  var point = Point();

  point.x = 4; // x에 대한 setter 메서드 사용

  assert(point.x == 4); // x에 대한 getter 메서드 사용

  assert(point.y == null); 

}

 

다음은 final 멤버에 대한 초기화 예제입니다.

 

class ProfileMark {

  final String name;  // 언제 초기화???

  final DateTime start = DateTime.now(); // 실행시간에 초기화 

  ProfileMark(this.name);

  ProfileMark.unnamed() : name = '';

}

 

생성자(Constructors)

 

클래스와 이름이 같은 함수는 생성자를 의미합니다. 

 

class Point {

  double x = 0;

  double y = 0;

  Point(double x, double y) { // x, y 값을 전달받는 생성자 

    this.x = x; this.y = y;

  }

}

 

this 예약어는 현재 작업할 인스턴스를 의미합니다.

 

다트는 아래 예처럼 생성자를 작성할 때 자동으로 해당 변수 초기화를 지원합니다.

 

class Point {

  double x = 0;

  double y = 0;

  Point(this.x, this.y);  // 생성자 호출 시 자동으로 x, y 초기화 

}

 

void main() {
  var p = Point(10, 20);
  print(p.x + p.y);   // 출력: 30 
}

 

기본 생성자 

 

생성자를 선언하지 않으면 기본 생성자가 제공됩니다. 기본 생성자는 인수가 없으며 수퍼 클래스에 있는 인수는 생성자를 호출합니다.

 

생성자는 상속되지 않음 

 

하위 클래스는 수퍼 클래스의 생성자를 상속받지 않습니다. 생성자가 없는 하위 클래스에는 암시적인 기본 생성자만 있습니다.

 

명명된 생성자(Named constructors)

 

명명된 생성자를 사용하여 클래스에 대해 여러 생성자를 구현할 수 있습니다.

 

const double xOrigin = 0;

const double yOrigin = 0;

 

class Point {

  double x = 0;

  double y = 0;

  Point(this.x, this.y);

 

  // 명명된 생성자 origin()

  Point.origin() :

    x = xOrigin,

    y = yOrigin;

}

 

기본 아닌 슈퍼 클래스의 생성자 호출

 

하위 클래스의 생성자는 수퍼 클래스의 기본 생성자를 호출합니다. 슈퍼 클래스의 생성자는 생성자 본문의 시작 부분에서 호출됩니다. 초기화 리스트를 사용중이면, 수퍼 클래스가 호출되기 전에 실행됩니다. 생성자 호출 순서는 다음과 같습니다.

  1. 초기화 리스트 (initializer list)
  2. 수퍼 클래스의 기본 생성자
  3. 주 클래스의 기본 생성자 

수퍼 클래스에 기본 생성자가 없으면 수퍼 클래스의 생성자 중 하나를 수동으로 호출해야 합니다. 다음 예제는 Employee 클래스의 생성자는 수퍼 클래스 Person에 명명된 생성자를 호출합니다.

 

class Person {
  String? firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person은 기본 생성자가 없음
  // super.fromJson(data)를 명시적으로 호출해야 한다.
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

void main() {
  var employee = Employee.fromJson({});
  print(employee);
}

 

[실행 결과]

in Person

in Employee

Instance of 'Employee'

 

슈퍼 클래스 생성자에 대한 인수는 생성자 호출 전에 평가되므로 인수는 함수 호출 같은 표현식도 가능합니다.

 

class Employee extends Person {

  Employee() : super.fromJson(fetchDefaultData());

  // ···

}

 

초기화 리스트(Initializer list)

 

슈퍼 클래스 생성자를 호출하는 것 외에도 생성자 본문 실행 전에 인스턴스 변수를 초기화 할 수 있습니다. 초기화는 쉼표로 구분합니다.

 

// 초기화 목록은 생성자 본문 실행 전에 인스턴스 변수를 설정

Point.fromJson(Map<String, double> json)

  :  x = json['x']!,

    y = json['y']! {

  print('In Point.fromJson(): ($x, $y)');

}

 

개발 중에 초기화 목록에서 assert를 사용하여 입력 유효성을 검사할 수 있습니다.

 

Point.withAssert(this.x, this.y) : assert(x >= 0) {

  print('In Point.withAssert(): ($x, $y)');

}

 

초기화 목록은 최종 필드를 설정할 때 편리합니다.

 

생성자 리다이렉션

 

때때로 생성자의 유일한 목적은 동일한 클래스의 다른 생성자로 리디렉션(전달)하는 것입니다. 리디렉션 생성자의 본문이 비어 있으며 생성자 호출은 콜론(:) 뒤에 표시합니다.

 

class Point {

  double x, y;

  // 이 클래스의 주 생성자

  Point(this.x, this.y);

  // 주 생성자에게 위임하기.

  Point.alongXAxis(double x) : this(x, 0);  // this는 주 생성자를 의미

}

 

상수 생성자

 

클래스가 절대 변경되지 않는 객체를 생성하려는 경우, 컴파일 시간 상수로 만들 수 있습니다. 단, const 생성자를 정의하고 모든 인스턴스 변수가 final, const인지 확인하기 바랍니다.

 

class ImmutablePoint {

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

  final double x, y;

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

}

 

factory(팩토리) 생성자

 

클래스의 동일한 인스턴스만 만드는 생성자를 구현할 때 factory 키워드를 사용합니다. 예를 들어 팩토리 생성자는 캐시에서 인스턴스를 반환하거나 하위 유형의 인스턴스를 반환할 수 있습니다. 팩토리 생성자의 또 다른 예는 초기화 리스트에서 처리 할 수없는 로직을 사용하여 최종 변수를 초기화하는 경우입니다.

 

다음 예제에서 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);

 

참고: 팩토리 생성자는 this를 사용할 수 없습니다. 

 

<이상> 

 

반응형

'Dart' 카테고리의 다른 글

Dart, 클래스 class 확장  (0) 2021.06.17
Dart, 메서드(메소드)  (0) 2021.06.17
Dart, 예외 처리 try ~ catch  (0) 2021.06.15
Dart, 디버깅 함수 assert()  (0) 2021.06.15
Dart, 흐름 제어 if, for, while, do while, switch  (0) 2021.06.15

+ Recent posts