Flutter開發之Dart中的類和對象

dart-logo

  • 原文博客地址: Flutter和Dart系列文章
  • 上次學習Flutter已是整整一年前的事情了,以後由於工做重心主要放在了React Native開發形式上
  • 如今從新撿起Flutter, 也是計劃系統性的從頭開始從新學習DartFlutter
  • 這篇Dart筆記主要就是記錄Dart語言中的類和對象

類及其構造方法

Dart也是一門面向對象的開發語言,面向對象中很是重要的概念就是類,經過類的初始化建立一個對象html

類的定義

  • Dart中,定義類用class關鍵字
  • 當未指明其父類的時候, 默認是繼承自Object的, 格式以下:
class 類名 {
  類型 成員名;
  返回值類型 方法名(參數列表) {
    方法體
  }
}
複製代碼
  • Dart語言中, 在類中使用屬性(成員/實例變量)時, 有必要時是經過this獲取的
  • 可是下面在getsize方法中並無加this
  • 這裏須要注意的是: Dart的開發風格中,在方法中一般使用屬性時,會省略this,可是有命名衝突時,this不能省略
// 建立類
class Point {
  // 定義變量
  int x;

  void getsize() {
    print('x = $x');
  }
}

// 類的初始化
main(List<String> args) {
    // 從Dart2開始,new關鍵字能夠省略
    var point = new Point();
    point.x = 1;
    point.getsize();
}
複製代碼

構造方法

  • 當經過類建立一個對象時,會調用這個類的構造方法
    • Dart語言中,若是類中沒有明確指定構造方法時,將默認擁有一個無參的構造方法()
    • 上面獲得的point對象調用的就是默認的無參構造方法
  • 也能夠根據本身的需求自定義構造方法
    • 當咱們建立了本身的構造方法時,默認的無參的構造方法將會失效,不能使用,不然會報錯
    • 由於Dart自己不支持函數的重載, 因此若是咱們明確的寫一個默認的構造方法,就會和咱們自定義的構造方法衝突
class Student {
  String name;
  int age;

  Student(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
複製代碼
  • 在上面構造方法中主要實現的就是經過構造函數的參數給類的戶型賦值
  • 爲了簡化這一過程, Dart提供了一種更加簡潔的語法糖形式
class Student1 {
  String name;
  int age;

  // 這裏和上面的Studeng的構造方法等價
  Student1(this.name, this.age);
}
複製代碼

命名構造方法

  • 在實際開發中, 很明顯一個構造方法的確是不夠咱們使用的
  • 並且Dart又不支持函數的重載, 不能建立愛你相同名稱不一樣參數的構造方法
  • 這就衍生出了另一中構造方法:命名構造方法
class Model {
  String name;
  int age;

  Model(this.name, this.age);

  // 命名構造方法
  Model.withNameAndAge(String name, int age) {
    this.name = name;
    this.age = age;
  }
  // 命名構造方法
  Model.initJson(Map<String, Object> map) {
    this.name = map['name'];
    this.age = map['age'];
  }
}


// 初始化對象
main() {
  // 普通構造方法
  var model0 = Model('name', 12);
  // 命名構造方法
  var model1 = Model.withNameAndAge('titan', 12);
  var model2 = Model.initJson({'name': 'jun', 'age': 18});
}
複製代碼

初始化列表

幾種方式定義的屬性都是可變的, 若是定義的屬性是final不可從新賦值的又該如何實現api

class Teacher {
  final String name;
  final int age;

  // 1. 這裏會有一個錯誤提示: All final variables must be initialized, but 'age' and 'name' are not
  Teacher(String name, int age) {
    //2. 這裏也會有一個錯誤提示: 'name' can't be used as a setter because it's final
    this.name = name;
    this.age = age;
  }
}
複製代碼
  • 上面第一處錯誤主要是由於: 在Dart中在執行下面{ }中的代碼的時候, 表示Teacher對象已經初始化完畢了
  • 因此在執行{ }以前, 必須保證nameage被初始化了
  • 並且final修飾的屬性是不可被從新賦值的, 因此纔會報錯
  • 或者也可使用函數中的命名可選參數處理
class Size {
  final double width;
  final double height;
  final double area;

  // 命名可選參數
  Size(this.width, this.height, { this.area = 10 }); 
}
複製代碼
  • 上面經過命名可選參數的形式, 給參數設置默認值也是能夠的, 可是不一樣的是area只能設置具體的數值, 不能設置表達式
  • 初始化列表的形式不但能夠設置具體的數值, 也能夠設置默認值爲表達式的形式
class Size {
  final double width;
  final double height;
  final double area;

  // 多個屬性使用逗號分隔
  Size(double width, double height): 
    this.width = width,
    this.height = height,
    this.area = width * height;
}
複製代碼

重定向構造方法

  • 下面的構造函數中, 咱們只能經過傳入兩個參數來獲取一個對象
  • 若是在某些狀況下, 但願只經過一個name變量來獲取一個對象
  • 這種狀況下, 就能夠經過在構造方法中去調用另一個構造方法, 這個時候可使用重定向構造方法
  • 須要注意的是: 在一個構造函數中,去調用另一個構造函數, 是在冒號後面使用this調用
class Point {
  String name;
  int age;

  Point(this.name, this.age);

  // 重定向的構造方法
  Point.fromName(String name): this(name, 0);
}

// 使用方法
var point = Point.fromName("name");
print(point.age);  // 輸出: 0
複製代碼

常量構造函數

  • 在某些狀況下, 咱們但願經過構造函數, 只要傳入相同的參數, 那麼獲得的對象就是同一個
  • Dart中判斷兩個對象是不是同一個的方法是經過函數identical判斷, 返回值是一個布爾值
// 普通構造函數
class Person {
  String name;
  int age;

  Person(this.name, this.age);
}

// 初始化列表
class Size {
  final double width;
  final double height;
  final double area;

  // 多個屬性使用逗號分隔
  Size(double width, double height): 
    this.width = width,
    this.height = height,
    this.area = width * height;
}

main(List<String> args) {
  var p1 = Person("name", 10);
  var p2 = Person("name", 10);
  // 判斷兩個對象是否是同一個
  print(identical(p1, p2));    /// false


  var s1 = Size(10, 20);
  var s2 = Size(10, 20);
  // 判斷兩個對象是否是同一個
  print(identical(s1, s2));    /// false
}
複製代碼
  • 很明顯上面兩種方式初始化的對象都不是同一個
  • 其實在Dart中若是將構造方法前加const進行修飾,那麼能夠保證相同的參數,建立出來的對象是相同的
  • 這樣的構造方法就稱之爲常量構造方法
// 常量構造方法
class Teacher {
  final String name;

  const Teacher(this.name);
}

main(List<String> args) {
  // 常量構造方法
  // 這裏的const不能夠省略
  var t1 = const Teacher("name");
  var t2 = const Teacher("name");
  print(identical(t1, t2));    /// true
  
  // 這裏的const能夠省略
  const t3 = Teacher("name");
  const t4 = Teacher("name");
  print(identical(t3, t4));    /// true
  print(identical(t1, t4));    /// true
}
複製代碼

常量構造方法有一些注意點:緩存

  • 擁有常量構造方法的類中,全部的成員變量必須是final修飾的.
  • 爲了能夠經過常量構造方法,建立出相同的對象,再也不使用new關鍵字,而是使用const關鍵字
  • 若是是將結果賦值給const修飾的標識符時,const能夠省略.

工廠構造方法

  • Dart提供了factory關鍵字, 用於經過工廠去獲取對象
  • 普通的構造函數, 會默認返回建立出來的對象, 不須要咱們手動return
  • 工廠構造方法, 須要手動返回一個對象
  • 一樣和上面同樣的目的, 只要傳入相同的參數, 那麼獲得的對象就是同一個, 下面經過工廠構造函數的方式實現
main(List<String> args) {
  
  var p1 = Person.fromName("titan");
  var p2 = Person.fromName("titan");
  print(identical(p1, p2)); // true
}

class Person {
  String name;

  // 用於緩存建立的對象, 避免大量的建立和銷燬對象
  static final Map<String, Person> _cache = <String, Person>{};

  factory Person.fromName(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final p = Person(name);
      _cache[name] = p;
      return p;
    }
  }

  Person(this.name);
}
複製代碼

類的繼承

setter和getter

  • Dart中類定義的屬性默認是能夠直接被外界訪問的
  • Dart中也存在settergetter方法, 用於監聽累的屬性被訪問的過程
main() {
  var people = People('top');
  people.setName = 'top';
  print(people.getName);
  print(people.name);

  var person = Person('titan');
  person.setName = 'jun';
  print(person.getName);
}


class People {
  String name;

  // setter
  set setName(String value) {
    this.name = value;
  }
  // getter
  String get getName {
    return 'titanjun';
  }

  People(this.name);
}
複製代碼
  • 上面setNamegetName是自定義的, 你也能夠命名爲setterNamegetterName
  • 還有就是上述兩個方法不是系統自動生成的, 是須要咱們手動添加的
  • 簡單的方式也可使用箭頭函數
class Person {
  String name;

  // setter
  set setName(String value) => this.name = value;
  // getter
  String get getName => 'titanjun';

  Person(this.name);
}
複製代碼

類的繼承

  • Dart中一樣支持類的繼承, 繼承使用extends關鍵字,子類中使用super來訪問父類
  • 父類中除了構造方法外, 全部的成員變量和方法都會被繼承
  • 子類能夠擁有本身的成員變量, 而且能夠對父類的方法進行重寫
  • 子類中能夠調用父類的構造方法,對某些屬性進行初始化:
    • 子類的構造方法在執行前,將隱含調用父類的無參默認構造方法(沒有參數且與類同名的構造方法)
    • 若是父類沒有無參默認構造方法,則子類的構造方法必須在初始化列表中經過super顯式調用父類的某個構造方法
class People {
  String name;

  People(this.name);

  void eat() {
    print('people -- eat');
  }
}

class Person extends People {
  int age;

  Person(String name, int age): this.age = age, super(name);

  @override
  void eat() {
    // 這裏的super, 看我的需求是否調用
    super.eat();
    print('Person -- eat');
  }
}

main(List<String> args) {
  var people = People('name');
  people.eat();

  var person = Person("top", 10);
  person.eat();
}
複製代碼

抽象類

  • Dart中抽象類是使用abstract聲明的類
  • Dart中沒有具體實現的方法(沒有方法體),就是抽象方法
  • 抽象方法,必須存在於抽象類中, 抽象類不能實例化
  • 抽象類中的抽象方法必須被子類實現, 抽象類中的已經被實現方法, 能夠不被子類重寫
abstract class Size {
  int width;
  int height;

  Size(this.width, this.height);

  void getSize();

  int getArea() {
    return this.width * this.height;
  }
}

class Area extends Size {
  @override
  void getSize() {
    print('width = $width, height = $height');
  }

  Area(int width, int height): super(width, height);
}

main(List<String> args) {
  // 實例化Size會報錯: Abstract classes can't be instantiated
  // var size = Size(20, 2);
  var area = Area(10, 2);
  area.getArea();
  print(area.getArea());
}
複製代碼

類成員和方法

Dart中咱們使用static關鍵字來定義類成員和類方法微信

main() {
 var person = Person();
 print(person.firstName);
 person.hide();

 print(Person.lastName);
 Person.show();
}


class Person {

  String firstName = 'top';
  // 不能使用this調用靜態屬性
  static String lastName = 'titanjun';


  void hide() {
    print('titanjun');
  }

  // 靜態方法
  static void show() {
    print('https://www.$lastName.top');
  }
}
複製代碼

多繼承

Dart中只有單繼承, 是不支持多繼承的, 可是咱們卻能夠經過其餘方式間接實現多繼承問題markdown

隱式接口

  • Dart中的接口比較特殊, 沒有一個專門的關鍵字來聲明接口, 默認狀況下全部的類都是隱式接口
  • 默認狀況下,定義的每一個類都至關於默認也聲明瞭一個接口,能夠由其餘的類來實現
  • Dart開發中,咱們一般將用於給別人實現的類聲明爲抽象類
  • 當將一個類可以作接口使用時, 那麼實現這個接口的類, 必須實現這個接口中的全部方法
  • Dart中經過implements來實現多繼承問題, 可是必須實現這個接口中的全部方法, 並且在方法的實現中不能調用super方法
abstract class Woman {
  void eat();

  void student() {
    print("student");
  }
}

class Man {
  void run() {
    print("runner");
  }
}


class Student implements Woman, Man {
  @override
  void eat() {
    print("eat");
  }

  @override
  void student() {
    print("student--student");
  }

  @override
  void run() {
    // 這裏不能調用super方法
    // super.run(); 
    print("run");
  }
}

main(List<String> args) {
  var stu = Student();
  stu.eat();
  stu.run();
  stu.student();
}
複製代碼

Mixin混入

  • 在經過implements實現某個類時,類中全部的方法都必須被從新實現(不管這個類原來是否已經實現過該方法)
  • 可是某些狀況下,一個類可能但願直接複用以前類的原有實現方案
  • Dart提供了另一種方案: Mixin混入的方式
    • 除了能夠經過class定義類以外,也能夠經過mixin關鍵字來定義一個類。
    • 只是經過mixin定義的類用於被其餘類混入使用,經過with關鍵字來進行混入
mixin Runner {
  run() {
    print('在奔跑');
  }
}

mixin Flyer {
  fly() {
    print('在飛翔');
  }
}

// 這裏能夠對原方法不作任何實現
class Bird with Runner, Flyer {  }

main(List<String> args) {
  var bird = Bird();
  bird.run();
  bird.fly();
}
複製代碼

參考資料


歡迎您掃一掃下面的微信公衆號,訂閱個人博客!ide

微信公衆號
相關文章
相關標籤/搜索