dart2筆記-類

桃花林裡練醉拳發表於2019-01-16

基本概念

  • Object類:Dart是一個物件導向程式語言,每個物件都是一個類的例項,所有的類都繼承於Object。
//定義一個類
class Person {}
複製程式碼
  • 類的繼承性:每個類(Object 除外) 都只有一個超類(父類),一個類的程式碼可以被其他多個類繼承來使用。
  • 建立新物件:使用new 關鍵字和建構函式來,建構函式名字可以類名;
  • 物件的成員:包括方法和資料 (函式和示例變數),使用點(.)來引用物件的變數或者方法,使用 ?. 來替代 . 來訪問物件可以避免當左邊物件為 null 時候丟擲異常.
// If p is non-null, set its y value to 4.
p?.y = 4;
複製程式碼
  • 獲取例項物件的型別:可以使用 Object 的 runtimeType 屬性來判斷例項的型別,該屬性返回一個 Type物件。
class Student {}
main() {
  var a = new Student();
  print(a.runtimeType); //Student
}
複製程式碼

類變數和函式

靜態變數
  • 靜態變數:static關鍵字修飾的類級別的變數,直接使用"類名.x"來呼叫;
  • 靜態變數使用lowerCamelCase 來命名常量。
  • 靜態變數在定義的時候可以不初始化,此時預設為null。
class Person {
  static String name;
  static const sex = "男";
}

main() {
  print(Person.name);
  print(Person.sex);
}
複製程式碼
靜態函式

static 修飾的函式即靜態函式,靜態函式不再類例項上執行, 所以無法訪問 this。

注意: 對於通用的或者經常使用的靜態函式,考慮 使用頂級方法而不是靜態函式。

class Person {
  static void printInfo(){
    print("hello world");
  }
}

main() {
  Person.printInfo();
}
複製程式碼

例項的私有屬性

和Java不同的是,Dart沒有public、protected、和private關鍵字。如果一個識別符號以 (_) 開頭,則該識別符號在庫內是私有的。

例項變數

  • 類的例項變數,所有沒有初始化的變數值都是null。
  • 如果你在例項變數定義的時候初始化該變數(不是 在建構函式或者其他方法中初始化),改值是在例項建立的時候 初始化的,也就是在建構函式和初始化引數列 表執行之前。
  • 每個例項變數都會自動生成一個getter和setter方法(隱含的)。
class Person {
  //類的例項變數
  String name;
  String sex;
  int age;
}

main() {
  var p = new Person();
  p.age = 24; // Use the setter method for x.
  print(p.age.toString());
}
複製程式碼

this關鍵字

this 關鍵字指當前的例項,只有當名字衝突的時候才使用 this,Dart程式碼風格樣式推薦忽略 this

class Point {
  num x;
  num y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}
複製程式碼

建構函式賦值例項變數簡化寫法:

class Point {
  num x;
  num y;

  //由於把建構函式引數賦值給例項變數的場景太常見了, Dart 提供了一個語法糖來簡化這個操作:
  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}
複製程式碼

函式

函式是類中定義的方法,是類物件的行為。

例項函式

例項函式是類中定義的普通方法,類例項物件可以呼叫的方法,物件的例項函式可以訪問this;

class Person {
  String name;

  printName() {
    print(this.name);
  }
}

main() {
  new Person()
    ..name = 'ethan'
    ..printName();
}
複製程式碼
getter and setter

getter 和 setter 是用來設定和訪問物件屬性的特殊函式,定義時需要在變數名前加get或set標識。

特點:

  • 每個例項變數預設隱含一個getter,如果變數不是final型別,則還會隱含一個 setter。
  • 可以通過getter 和 setter 來建立新的屬性,使用 get 和 set 關鍵字定義 getter和setter;
  • 如果定義一個帶get標識的變數,則例項物件可以直接通過“物件.x”來獲取變數,但是這個變數沒有setter,此時必須也實現setter,才可以通過“物件.x=n”來改變此變數;
class Person {
  String name;
  String prefix;

  Person(this.name);

  String get description => prefix + " " + name;
  set description(String prefix) => this.prefix = prefix;
}

main() {
  var p = new Person("ethan");
  p.description = 'x';
  print(p.description);
}
複製程式碼
抽象函式

抽象函式是沒有函式體的函式。

例項函式、 getter、和 setter 函式可以為抽象函式, 抽象函式是隻定義函式介面但是沒有實現的函式,由子類來 實現該函式。

示例:

//抽象類
abstract class Doer {
  //抽象方法
  void doSomething();
}

//子類繼承抽象類,必須實現抽象方法
class EffectiveDoer extends Doer {
  void doSomething() {
    print("hello");
  }
}
複製程式碼

建構函式

概述

定義一個和類名字一樣的方法就定義了一個建構函式。 建構函式用來生成一個物件的新例項;

  • 預設建構函式:與類同名,配合new關鍵字建立例項。如果沒有定義建構函式,則會有個預設建構函式。 預設建構函式沒有引數,並且會呼叫超類的 沒有引數的建構函式。
  • 建構函式不會繼承:子類不會繼承超類的建構函式。 子類如果沒有定義建構函式,則只有一個預設建構函式 (沒有名字沒有引數)。
  • 命名建構函式:定義格式為“類名.函式名(...)”,建立物件例項時也使用“類名.函式名(...)”.使用命名建構函式可以為一個類實現多個建構函式, 或者使用命名建構函式來更清晰的表明你的意圖;
class Person {
  //類的例項變數
  String name;
  int age;

  //預設建構函式
  Person();
  // Person(this.name, this.age);

  //命名建構函式
  Person.fromJson(Map jsonMap) {
    name = jsonMap['name'];
    age = jsonMap['age'];
  }
}

main() {
  var p = Person.fromJson({'name': "ethan", "age": 11});
  print(p.name);
  print(p.age);
}
複製程式碼
呼叫超類建構函式
預設呼叫方式

子類的建構函式會自動呼叫超類的 無名無引數的預設建構函式。 超類的建構函式在子類建構函式體開始執行的位置呼叫。 如果提供了一個 initializer list(初始化引數列表) ,則初始化引數列表在超類建構函式執行之前執行。

下面是建構函式執行順序:

1. initializer list(初始化引數列表)
2. superclass’s no-arg constructor(超類的無名建構函式)
3. main class’s no-arg constructor(主類的無名建構函式)
複製程式碼
超類是無引數建構函式

如果超類沒有無名無引數建構函式, 則你需要手工的呼叫超類的其他建構函式。 在建構函式引數後使用冒號 (:) + super.xxx() 可以呼叫 超類建構函式。

class Person {
  String firstName;

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

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});
    //in Person
    //in Employee
}
複製程式碼

注意:

  • 如果在建構函式的初始化列表中使用 super(),需要把它放到最後。
  • 呼叫超類建構函式的引數無法訪問 this。 例如,引數可以為靜態函式但是不能是例項函式。
建構函式初始化列表

在建構函式體執行之前除了可以呼叫超類建構函式之外,還可以 初始化例項引數。 使用逗號分隔初始化表示式。

警告: 初始化表示式等號右邊的部分不能訪問 this。

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Initializer list sets instance variables before
  // the constructor body runs.
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}
複製程式碼
重定向建構函式

有時候一個建構函式會調動類中的其他建構函式。 一個重定向建構函式是沒有程式碼的,在建構函式宣告後,使用 冒號呼叫其他建構函式。

class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}
複製程式碼
常量建構函式

如果你的類提供一個狀態不變的物件,你可以把這些物件 定義為編譯時常量。要實現這個功能,需要定義一個 const 建構函式, 並且宣告所有類的變數為 final。

  • 使用常量建構函式 可以建立編譯時常量,要使用常量建構函式只需要用const替代new 即可;
  • 兩個使用常量建構函式建立,並且建立時的內容一樣常量物件,其實是同一個物件;
class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
}

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!
複製程式碼
工廠方法建構函式

如果一個建構函式並不總是返回一個新的物件,則使用 factory 來定義 這個建構函式。例如,一個工廠建構函式 可能從快取中獲取一個例項並返回,或者 返回一個子型別的例項。

工廠建構函式無法訪問 this。

示例:定義工廠建構函式。

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

  //_cache: 靜態私有變數快取例項物件
  static final Map<String, Logger> _cache = <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      //獲取快取
      return _cache[name];
    } else {
      //快取
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  //命名建構函式
  Logger._internal(this.name);

  //改變flag的值
  void changeFlag() {
    flag = !flag;
  }
}

main() {
  var logger = new Logger('LOG');
  print("flag = " + logger.flag.toString());
  logger.changeFlag();
  //快取的logger物件
  print("flag = " + new Logger('LOG').flag.toString());
  //新的logger物件
  print("flag = " + new Logger('X').flag.toString());
}
複製程式碼
//執行結果:
flag = false
flag = true
flag = false
複製程式碼

可覆寫的操作符

下表中的操作符可以被覆寫:

<	+	|	[]
>	/	^	[]=
<=	~/	&	~
>=	*	<<	==
–	%	>>	 
複製程式碼
覆寫示例
物件加減

覆寫+-法:

class Vector {
  final int x;
  final int y;
  const Vector(this.x, this.y);

  /// Overrides + (a + b).
  Vector operator +(Vector v) {
    return new Vector(x + v.x, y + v.y);
  }

  /// Overrides - (a - b).
  Vector operator -(Vector v) {
    return new Vector(x - v.x, y - v.y);
  }
}

main() {
  final v = new Vector(2, 3);
  final w = new Vector(2, 2);

  // v == (2, 3)
  assert(v.x == 2 && v.y == 3);

  // v + w == (4, 5)
  assert((v + w).x == 4 && (v + w).y == 5);

  // v - w == (0, 1)
  assert((v - w).x == 0 && (v - w).y == 1);
}
複製程式碼
覆寫==

Dart 中的每個物件都有一個整數 hash 碼,這樣每個物件都 可以當做 map 的 key 來用。但是,你可以覆寫 hashCode getter 來自定義 hash 碼的實現,如果你這樣做了,你也需要 同時覆寫 == 操作符。相等的物件(使用 == 比較)的 hash 碼應該一樣。hash code 碼並不要求是唯一的, 但是應該具有良好的分佈形態。

如果你覆寫了 == ,則還應該覆寫物件的 hashCode getter 函式。

class Person {
  final String firstName, lastName;

  Person(this.firstName, this.lastName);

  // Override hashCode using strategy from Effective Java,
  // Chapter 11.
  int get hashCode {
    int result = 17;
    result = 37 * result + firstName.hashCode;
    result = 37 * result + lastName.hashCode;
    return result;
  }

  // You should generally implement operator == if you
  // override hashCode.
  bool operator ==(other) {
    if (other is! Person) return false;
    Person person = other;
    return (person.firstName == firstName &&
        person.lastName == lastName);
  }
}

main() {
  var p1 = new Person('bob', 'smith');
  var p2 = new Person('bob', 'smith');
  var p3 = 'not a person';
  assert(p1.hashCode == p2.hashCode);
  assert(p1 == p2);
  assert(p1 != p3);
}
複製程式碼

抽象類

使用“abstract”關鍵字修飾的類為抽象類,不能例項化,抽象類的方法可以實現也可以不實現;

  • 如果你希望你的抽象類 是可示例化的,則定義一個 工廠建構函式。
  • 抽象方法即沒有方法體的方法,普通類不能有抽象方法;
//抽象類
abstract class Person {
  void eat();
}

//子類實現抽象類的方法
class Student extends Person {
  void eat(){
    print("i can eat");
  }
}

main() {
  new Student().eat();
}
複製程式碼

類的隱式介面

  • 每個類都隱式的定義了一個包含所有例項成員的介面, 並且這個類實現了這個介面。
  • 一個類可以通過 implements 關鍵字來實現一個或者多個介面, 並實現每個介面定義的 API。

如果你想 建立類 A 來支援 類 B 的 api,而不想繼承 B 的實現, 則類 A 應該實現 B 的介面。例如:

class Person {
  final _name;

  Person(this._name);

  String greet(who) => 'hello, $who. I am $_name.';
}

class Animal {
  void eat() {}
}

//實現多個介面
class StudentImpl implements Person, Animal {
  final _name = "";
  String greet(who) => 'Hi $who. Do you know who I am?';

  void eat() {
    print("i can eat");
  }
}

greetBob(Person person) => person.greet('bob');

main() {
  print(greetBob(new Person('kathy')));
  print(greetBob(new StudentImpl()));
}
複製程式碼

類繼承

  • 子類通過extends來繼承父類;
  • 通過super來呼叫父類方法;
  • 可以使用 @override註解一個函式,來表明你的函式是想覆寫超類的一個函式;

繼承示例如下:

class Animal {
  void eat() {
    print("i can eat");
  }

  void greet() {
    print("hello everyone !");
  }
}

class Person extends Animal {
  void printFeature() {
    super.eat();
  }

  //重寫父類函式
  @override
  void greet() {
    print("hello i am person");
  }
}

main() {
  new Person().greet();
}
複製程式碼

列舉型別

列舉型別通常稱之為 enumerations 或者 enums, 是一種特殊的類,用來表現一個固定 數目的常量。

  • 使用 enum 關鍵字來定義列舉型別;
  • 列舉型別中的每個值都有一個 index getter 函式, 該函式返回該值在列舉型別定義中的位置(從 0 開始)。
  • 列舉的 values 常量可以返回 所有的列舉值。
  • 可以在 switch 語句 中使用列舉。 如果在 switch (e) 中的 e 的型別為列舉類, 如果你沒有處理所有該列舉型別的值的話,則會丟擲一個警告;
  • 列舉類無法繼承列舉型別、無法使用 mix in、無法實現一個列舉型別;
  • 列舉類無法顯示的初始化一個列舉型別;
enum Color { red, green, blue }

judgeType(Color color) {
  switch (color) {
    case Color.blue:
      print("yes i am blue");
      break;
    default:
      print("no");
      break;
  }
}

main() {
  print(Color.green.index); // 1
  print(Color.values); //列舉值列表[Color.red, Color.green, Color.blue]
  //列舉應用switch
  judgeType(Color.red);
  judgeType(Color.blue);
}
複製程式碼

mixins重用類

mixins可以實現類似繼承多個類功能的效果,重用多個類的屬性。

  • mixins使用with關鍵字後跟需要的類;
  • Dart 1.13之前,定義一個類繼承Object,該類沒有建構函式,不能呼叫 super ,則該類就是一個 mixin。
  • Dart 1.13+,mixins可以繼承其他類,不再限制為繼承Object,可以呼叫 super()。

super mixins” 還 無法在 dart2js 中使用 並且需要在 dartanalyzer 中使用 --supermixin 引數。

class Animal {
  final name = "ethan";
}

class Musical {
  void printFeature() {
    print("i can song");
  }
}

class Person {
  String iq;
}

class Student extends Person with Animal, Musical {
  void printInfo() {
    print("name=" + name);
    super.printFeature();
    printFeature();
    super.iq = "100";
    print("IQ=" + iq);
  }
}

main() {
  new Student().printInfo();
}
複製程式碼

相關文章