Dart 언어 여행하기 요약(3)

Dart A Tour of the Dart Language https://www.dartlang.org/guides/language/language-tour

  • Classes
    • 개요
      • Dart 클래스는 단일상속과 mixin을 지원한다. 모든 클래스는 Object의 후손이다.
    • Using class members
    • Using constructors
      • var p1 = Point(2, 2); // 일반 생성자
        var p2 = Point.fromJson({'x': 1, 'y': 2}); // named 생성자
        
      • var p = const ImmutablePoint(2, 2); // 생성자 앞에 const를 붙이면 컴파일타임에 상수로 만들어버린다. 상수화가 가능한 클래스가 되려면 조건을 만족해야 한다.
      • var a = const ImmutablePoint(1, 1);
        var b = const ImmutablePoint(1, 1);
        assert(identical(a, b)); // 상수화된 두 객체는 정확히 일치한다.
        
      • const pointAndLine = const {
          'point': const [const ImmutablePoint(0, 0)],
          'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
        };
        const pointAndLine = { // 변수 선언 시 const만 지정해도 초기화 값들 전부 상수가 된다.
          'point': [ImmutablePoint(0, 0)],
          'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
        };
        
    • Getting an object’s type
      • print('The type of a is ${a.runtimeType}'); // 객체의 타입(Type객체)을 가져온다
    • Instance variables
      • final이 아닌 인스턴스 변수는 getter와 setter가 암시적으로 생성된다.
      • final인 인스턴스 변수는 getter만 생성된다.
      • assert(point.x == 4); // x를 위한 getter 메소드를 사용한 예
    • Constructors
      • class Point {
          num x, y;
          Point(num x, num y) {
            this.x = x;
            this.y = y;
          }
          Point(this.x, this.y); // 위 생성자와 같은 동작을 한다.
        }
        
      • sub class는 super class의 생성자를 상속하지 않는다.
      • // named 생성자를 사용하면 생성자의 의미가 더 명확해진다.
        class Point {
          num x, y;
          Point(this.x, this.y);
          Point.origin() {
            x = 0;
            y = 0;
          }
        }
        
      • Initializer list, super class 생성자, sub class 생성자 순으로 실행된다.
      • // sub class의 생성자에서 super class의 생성자를 명시적으로 호출할 수 있다.
        Employee.fromJson(Map data) : super.fromJson(data) { 
          print('in Employee');
        }
        
      • // 생성자 body가 실행되기 전에 initializer list나 assert를 실행할 수 있다.
        // initializer list는 final 멤버변수를 초기화할 때 유용하다.
        Point.fromJson(Map<String, num> json) : x = json['x'], y = json['y'] {
          print('In Point.fromJson(): ($x, $y)');
        }
        Point.withAssert(this.x, this.y) : assert(x >= 0) {
          print('In Point.withAssert(): ($x, $y)');
        }
        
      • Point.alongXAxis(num x) : this(x, 0); // 생성자를 redirecting 할 수 있다.
      • // 클래스의 모든 멤버가 const이면 컴파일 타임에 const 객체로 만들 수 있는 조건을 충족한다.
        class ImmutablePoint {
          static final ImmutablePoint origin = const ImmutablePoint(0, 0);
          final num x, y;
          const ImmutablePoint(this.x, this.y);
        }
        
      • // 싱글톤이나 풀을 만들고 싶을 때 factory 생성자를 사용한다.
        factory Logger(String name) {
          if (_cache.containsKey(name)) {
            return _cache[name];
          } else {
            final logger = Logger._internal(name);
            _cache[name] = logger;
            return logger;
          }
        }
        
    • Method
      • 암시적으로 생성되는 getter와 setter는 나중에 wrapping해야 할 상황이 생길 때 유용하다.
      • abstract class Doer {
          // abstract 메소드를 가지면 abstract class가 된다.
          void doSomething();
        }
        
    • Abstract class
      • factory 생성자를 사용하면 abstract 클래스는 인스턴스화 할 수 있다.
    • Implicit interfaces
      • 모든 class는 자신의 인스턴스 멤버를 포함하는 interface를 암시적으로 정의한다. 만약 class A를 지원하는 class B를 만들어서 다형성을 이용하려면 class B implements A와 같이 구현하면 된다.
      • class Person {
          final _name;
          Person(this._name);
          String greet(String who) => 'Hello, $who. I am $_name.';
        }
        class Impostor implements Person {
          get _name => ''; // _name의 getter를 구현한다.
          String greet(String who) => 'Hi $who. Do you know who I am?'; // greet()를 구현한다.
        }
        String greetBob(Person person) => person.greet('Bob'); // 다형성을 이용하는 함수
        void main() {
          print(greetBob(Person('Kathy')));
          print(greetBob(Impostor()));
        }
        
    • Extending a class
      • class SmartTelevision extends Television { // 상속
          @override // override 어노테이션
          void turnOn() {...}
        }
        
      • 눈에 띄는 overridable 연산자
        • []
        • []=
        • == : ==를 override 하려면 hashcode의 getter도 override 해야 한다.
        • +
      • class A {
          // Unless you override noSuchMethod, using a
          // non-existent member results in a NoSuchMethodError.
          @override
          void noSuchMethod(Invocation invocation) {
            print('You tried to use a non-existent member: ' +
              '${invocation.memberName}');
          }
        }
        
      • noSuchMethod forwarding specification
    • Enumerated types
      • enum Color { red, green, blue }
        assert(Color.red.index == 0);
        
    • Adding features to a class: mixins
      • mixin은 클래스 코드를 재사용할 수 있게 해주는 방법이다.
      • // mixin을 구현하려면 Object만 상속해야 하고, 생성자가 없어야 한다.
        mixin Musical {
          bool canPlayPiano = false;
          bool canCompose = false;
          bool canConduct = false;
          void entertainMe() {
            if (canPlayPiano) {
              print('Playing piano');
            } else if (canConduct) {
              print('Waving hands');
            } else {
              print('Humming to self');
            }
          }
        }
        
      • // 특정 클래스만 mixin 될 수 있게 하려면 on 키워드를 사용한다.
        mixin MusicalPerformer on Musician {
          // ···
        }
        
    • Class variables and methods
      • 정적 변수, 정적 메소드 지원
      • Dart 스타일 가이드는 상수를 lowerCamelCase로 이름짓기를 권장한다.
      • 공통적으로, 유틸리티처럼 사용되길 원한다면 정적 메소드 대신에 일급함수를 고려하자.