天天看點

Dart文法快速上手五 《Dart2之類與構造方法》

使用構造方法

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

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

上面這兩種寫法都是可以的,Dart2中new關鍵字已經成為一個可選項

常量構造方法

有些類提供常量構造函數。要使用常量構造函數建立編譯時常量,請将const關鍵字放在構造函數名稱之前

var p = const ImmutablePoint(2, 2);
           

構造兩個相同的編譯時常量會産生一個規範的執行個體:

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

assert(identical(a, b)); // They are the same instance!
           

如果将const關鍵字前置的話,後面的const是可以省略的,比如

// Lots of const keywords here.
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
           

上面的代碼可以寫成

// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
           

擷取運作時類型

要在運作時擷取對象的類型,可以使用Object的runtimeType屬性,該屬性傳回Type對象。

print('The type of a is ${a.runtimeType}');
           

####聲明構造函數

class Point {
  num x, y;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}
           

上面是一種簡化版的構造函數,隻是為了指派,是以省略了方法體

如果您未聲明構造函數,則會為您提供預設構造函數。

預設構造函數沒有參數,并在超類中調用無參數構造函數。

####命名構造函數

使用命名構造函數為類實作多個構造函數或提供額外的清晰度:

class Point {
  num x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}
           

跟Java等語言不同,Dart的多重構造方法是一種類名.自定義方法名來實作的,如果你按照傳統的方法來寫的話,例如這樣:

class Grammar2 {
  var x, y, z;

  Grammar2(this.x,this.y);  //錯誤

  Grammar2(this.x); //不能同時擁有兩個這樣的構造函數

}

           

正确的寫法

class Grammar2 {
  var x, y, z;

  Grammar2.translate(this.x, this.y); //TODO 構造方法的重載

  Grammar2(this.x,this.y);
  

   printRealVar() {
    print('x=' + x.toString() + "y=" + y.toString());
    return 2;
  }

 @override
  String toString() {
    // TODO: implement toString
    return "this is Grammar2";
  }
}

           

調用非預設的超類構造函數

預設情況下,子類中的構造函數調用超類的未命名的無參數構造函數。

超類的構造函數在構造函數體的開頭被調用。

如果還使用初始化清單,則在調用超類之前執行。

總之,執行順序如下:

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

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}
           

這段代碼的執行結果就是:

in Person

in Employee

除了調用超類構造函數之外,還可以在構造函數體運作之前初始化執行個體變量。

用冒号分隔初始化程式。

// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}
           

這個示例中,我們在調用fromjson這個構造方法之前我們搶先初始化了x和y的值,中間用冒号隔開

你也可以在調用初始化方法之前進行一些參數的驗證,比如:

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

這個示例中,構造方法調用之前,我們驗證了x必須為正數

構造函數的重定向

我們在開發中經常會有這樣的情景,我們構造了好幾個構造方法,但是這些構造方法本身并不執行任何操作,隻是為了調用其他構造 方法,在Dart中,我們稱之為構造方法的重定向(這個文法在前面示例中出現過)

class Point {
  num x, 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關鍵字(是不是很像單例模式?)

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

  final num x, y;

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

工廠構造方法

跟工廠設計模式類似,需要什麼給你什麼,根據一個特定的辨別産生不同的執行個體,在Dart中,通過傳入一個特定的辨別符,來檢視我的靜态緩存裡面有沒有這個緩存,如果有,直接傳回,如果沒有,我便new 一個對象存入緩存再傳回

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) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
           
工廠構造函數無權通路 this

對象之間的操作符運算

您可以覆寫下表中顯示的運算符。

例如,如果定義Vector類,則可以定義一個+方法來添加兩個向量。(翻譯自官網)

Dart文法快速上手五 《Dart2之類與構造方法》

來個示例,自己體會

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown. For details, see note below.
  // ···
}

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

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
           

要在代碼嘗試使用不存在的方法或執行個體變量時檢測或做出反應,您可以覆寫noSuchMethod():

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

枚舉類

enum Color { red, green, blue }

枚舉中的每個值都有一個索引getter,它傳回枚舉聲明中值的從零開始的位置。

例如,第一個值具有索引0,第二個值具有索引1。

assert(Color.red.index == 0);

assert(Color.green.index == 1);

assert(Color.blue.index == 2);

要擷取枚舉中所有值的清單,請使用枚舉值常量。

List colors = Color.values;

assert(colors[2] == Color.blue);

switch case中使用枚舉

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // Without this, you see a WARNING.
    print(aColor); // 'Color.blue'
}
           

Mixin

Mixins是一種在多個類層次結構中重用類代碼的方法,B繼承于A,但是B也要用到C的一些特性,多繼承在很多語言中是行不通的,Dart中的Mixin就是為了解決這種問題出現的

要使用mixin,請使用with關鍵字,後跟一個或多個mixin名稱。

以下示例顯示了兩個使用mixins的類:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
           

注意:要實作一個mixin,建立一個擴充Object的類,聲明沒有構造函數,并且沒有調用super。

例如:

abstract class 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');
    }
  }
}
           

泛型方法(隻看代碼不解釋,跟java幾乎一樣)

T first<T>(List<T> ts) {
  // Do some initial work or error checking, then...
  T tmp = ts[0];
  // Do some additional checking or processing...
  return tmp;
}
           

導入

Dart目前都要手動導入包,Dart内置的library可以直接用import ‘Interface.dart’;方法導入,自定義的包可以在前面加入一個package關鍵字導入

import 'package:flutter_app/Grammar2.dart';
import 'SuperGrammar.dart';
import 'Interface.dart';
import 'FactoryClass.dart';
import 'package:meta/meta.dart';
import 'ConstantClass.dart';
           

有一種情況例外,加入導入的兩個包裡面有同名的類,這個時候如何區分,java 是用全路徑來辨別,Dart裡面可以給這個包裡面所有的類設定一個别名

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;  //這裡利用as來設定 别名

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
           

如果你想導入的是一個包中的部分類

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
           

設定這個功能的出發點是可以降低包體積麼??? 不得而知

improt 懶加載

這個是為了在使用導入的類的時候才會去載入這個包,主要是為了提升啟動效率

如果需要使用懶加載的功能,請使用deferred as 關鍵字

import 'package:greetings/hello.dart' deferred as hello;

//需要的時候調用别名.loadLibiary方法
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
           

在前面的這段代碼中 await 關鍵字的作用是暫停程式的運作直到包導入完成

  • 延遲庫的常量不是導入檔案中的常量。

    請記住,在加載延遲庫之前,這些常量不存在。

  • 您不能在導入檔案中使用延遲庫中的類型。

    相反,請考慮将接口類型移動到由延遲庫和導入檔案導入的庫。

  • Dart隐式地将loadLibrary()插入到使用deferred as namespace定義的命名空間中。loadLibrary()函數傳回Future。