Flutter開(kāi)發(fā)之Dart中的類和對(duì)象

dart-logo
  • 原文博客地址: Flutter和Dart系列文章
  • 上次學(xué)習(xí)Flutter已經(jīng)是整整一年前的事情了,之后因?yàn)楣ぷ髦匦闹饕旁诹?code>React Native開(kāi)發(fā)形式上
  • 現(xiàn)在重新?lián)炱?code>Flutter, 也是計(jì)劃系統(tǒng)性的從頭開(kāi)始重新學(xué)習(xí)DartFlutter
  • 這篇Dart筆記主要就是記錄Dart語(yǔ)言中的類和對(duì)象

類及其構(gòu)造方法

Dart也是一門面向?qū)ο蟮拈_(kāi)發(fā)語(yǔ)言,面向?qū)ο笾蟹浅V匾母拍罹褪穷悾ㄟ^(guò)類的初始化創(chuàng)建一個(gè)對(duì)象

類的定義

  • Dart中十偶,定義類用class關(guān)鍵字
  • 當(dāng)未指明其父類的時(shí)候, 默認(rèn)是繼承自Object的, 格式如下:
class 類名 {
  類型 成員名;
  返回值類型 方法名(參數(shù)列表) {
    方法體
  }
}
  • Dart語(yǔ)言中, 在類中使用屬性(成員/實(shí)例變量)時(shí), 有必要時(shí)是通過(guò)this獲取的
  • 但是下面在getsize方法中并沒(méi)有加this
  • 這里需要注意的是: Dart的開(kāi)發(fā)風(fēng)格中转捕,在方法中通常使用屬性時(shí),會(huì)省略this耗式,但是有命名沖突時(shí)胁住,this不能省略
// 創(chuàng)建類
class Point {
  // 定義變量
  int x;

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

// 類的初始化
main(List<String> args) {
    // 從Dart2開(kāi)始,new關(guān)鍵字可以省略
    var point = new Point();
    point.x = 1;
    point.getsize();
}

構(gòu)造方法

  • 當(dāng)通過(guò)類創(chuàng)建一個(gè)對(duì)象時(shí)刊咳,會(huì)調(diào)用這個(gè)類的構(gòu)造方法
    • Dart語(yǔ)言中,如果類中沒(méi)有明確指定構(gòu)造方法時(shí)彪见,將默認(rèn)擁有一個(gè)無(wú)參的構(gòu)造方法()
    • 上面得到的point對(duì)象調(diào)用的就是默認(rèn)的無(wú)參構(gòu)造方法
  • 也可以根據(jù)自己的需求自定義構(gòu)造方法
    • 當(dāng)我們創(chuàng)建了自己的構(gòu)造方法時(shí),默認(rèn)的無(wú)參的構(gòu)造方法將會(huì)失效娱挨,不能使用,否則會(huì)報(bào)錯(cuò)
    • 因?yàn)?code>Dart本身不支持函數(shù)的重載, 所以如果我們明確的寫(xiě)一個(gè)默認(rèn)的構(gòu)造方法余指,就會(huì)和我們自定義的構(gòu)造方法沖突
class Student {
  String name;
  int age;

  Student(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
  • 在上面構(gòu)造方法中主要實(shí)現(xiàn)的就是通過(guò)構(gòu)造函數(shù)的參數(shù)給類的戶型賦值
  • 為了簡(jiǎn)化這一過(guò)程, Dart提供了一種更加簡(jiǎn)潔的語(yǔ)法糖形式
class Student1 {
  String name;
  int age;

  // 這里和上面的Studeng的構(gòu)造方法等價(jià)
  Student1(this.name, this.age);
}

命名構(gòu)造方法

  • 在實(shí)際開(kāi)發(fā)中, 很明顯一個(gè)構(gòu)造方法的確是不夠我們使用的
  • 而且Dart又不支持函數(shù)的重載, 不能創(chuàng)建愛(ài)你相同名稱不同參數(shù)的構(gòu)造方法
  • 這就衍生出了另外一中構(gòu)造方法:命名構(gòu)造方法
class Model {
  String name;
  int age;

  Model(this.name, this.age);

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


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

初始化列表

幾種方式定義的屬性都是可變的, 如果定義的屬性是final不可重新賦值的又該如何實(shí)現(xiàn)

class Teacher {
  final String name;
  final int age;

  // 1. 這里會(huì)有一個(gè)錯(cuò)誤提示: All final variables must be initialized, but 'age' and 'name' are not
  Teacher(String name, int age) {
    //2. 這里也會(huì)有一個(gè)錯(cuò)誤提示: 'name' can't be used as a setter because it's final
    this.name = name;
    this.age = age;
  }
}
  • 上面第一處錯(cuò)誤主要是因?yàn)? 在Dart中在執(zhí)行下面{ }中的代碼的時(shí)候, 表示Teacher對(duì)象已經(jīng)初始化完畢了
  • 所以在執(zhí)行{ }之前, 必須保證nameage被初始化了
  • 而且final修飾的屬性是不可被重新賦值的, 所以才會(huì)報(bào)錯(cuò)
  • 或者也可以使用函數(shù)中的命名可選參數(shù)處理
class Size {
  final double width;
  final double height;
  final double area;

  // 命名可選參數(shù)
  Size(this.width, this.height, { this.area = 10 }); 
}
  • 上面通過(guò)命名可選參數(shù)的形式, 給參數(shù)設(shè)置默認(rèn)值也是可以的, 但是不同的是area只能設(shè)置具體的數(shù)值, 不能設(shè)置表達(dá)式
  • 初始化列表的形式不但可以設(shè)置具體的數(shù)值, 也可以設(shè)置默認(rèn)值為表達(dá)式的形式
class Size {
  final double width;
  final double height;
  final double area;

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

重定向構(gòu)造方法

  • 下面的構(gòu)造函數(shù)中, 我們只能通過(guò)傳入兩個(gè)參數(shù)來(lái)獲取一個(gè)對(duì)象
  • 如果在某些情況下, 希望只通過(guò)一個(gè)name變量來(lái)獲取一個(gè)對(duì)象
  • 這種情況下, 就可以通過(guò)在構(gòu)造方法中去調(diào)用另外一個(gè)構(gòu)造方法, 這個(gè)時(shí)候可以使用重定向構(gòu)造方法
  • 需要注意的是: 在一個(gè)構(gòu)造函數(shù)中,去調(diào)用另外一個(gè)構(gòu)造函數(shù), 是在冒號(hào)后面使用this調(diào)用
class Point {
  String name;
  int age;

  Point(this.name, this.age);

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

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

常量構(gòu)造函數(shù)

  • 在某些情況下, 我們希望通過(guò)構(gòu)造函數(shù), 只要傳入相同的參數(shù), 那么得到的對(duì)象就是同一個(gè)
  • Dart中判斷兩個(gè)對(duì)象是否是同一個(gè)的方法是通過(guò)函數(shù)identical判斷, 返回值是一個(gè)布爾值
// 普通構(gòu)造函數(shù)
class Person {
  String name;
  int age;

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

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

  // 多個(gè)屬性使用逗號(hào)分隔
  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);
  // 判斷兩個(gè)對(duì)象是不是同一個(gè)
  print(identical(p1, p2));    /// false


  var s1 = Size(10, 20);
  var s2 = Size(10, 20);
  // 判斷兩個(gè)對(duì)象是不是同一個(gè)
  print(identical(s1, s2));    /// false
}
  • 很明顯上面兩種方式初始化的對(duì)象都不是同一個(gè)
  • 其實(shí)在Dart中如果將構(gòu)造方法前加const進(jìn)行修飾让蕾,那么可以保證相同的參數(shù)浪规,創(chuàng)建出來(lái)的對(duì)象是相同的
  • 這樣的構(gòu)造方法就稱之為常量構(gòu)造方法
// 常量構(gòu)造方法
class Teacher {
  final String name;

  const Teacher(this.name);
}

main(List<String> args) {
  // 常量構(gòu)造方法
  // 這里的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
}

常量構(gòu)造方法有一些注意點(diǎn):

  • 擁有常量構(gòu)造方法的類中,所有的成員變量必須是final修飾的.
  • 為了可以通過(guò)常量構(gòu)造方法探孝,創(chuàng)建出相同的對(duì)象笋婿,不再使用new關(guān)鍵字,而是使用const關(guān)鍵字
  • 如果是將結(jié)果賦值給const修飾的標(biāo)識(shí)符時(shí)顿颅,const可以省略.

工廠構(gòu)造方法

  • Dart提供了factory關(guān)鍵字, 用于通過(guò)工廠去獲取對(duì)象
  • 普通的構(gòu)造函數(shù), 會(huì)默認(rèn)返回創(chuàng)建出來(lái)的對(duì)象, 不需要我們手動(dòng)return
  • 工廠構(gòu)造方法, 需要手動(dòng)返回一個(gè)對(duì)象
  • 同樣和上面一樣的目的, 只要傳入相同的參數(shù), 那么得到的對(duì)象就是同一個(gè), 下面通過(guò)工廠構(gòu)造函數(shù)的方式實(shí)現(xiàn)
main(List<String> args) {
  
  var p1 = Person.fromName("titan");
  var p2 = Person.fromName("titan");
  print(identical(p1, p2)); // true
}

class Person {
  String name;

  // 用于緩存創(chuàng)建的對(duì)象, 避免大量的創(chuàng)建和銷毀對(duì)象
  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中類定義的屬性默認(rèn)是可以直接被外界訪問(wèn)的
  • Dart中也存在settergetter方法, 用于監(jiān)聽(tīng)累的屬性被訪問(wèn)的過(guò)程
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
  • 還有就是上述兩個(gè)方法不是系統(tǒng)自動(dòng)生成的, 是需要我們手動(dòng)添加的
  • 簡(jiǎn)單的方式也可以使用箭頭函數(shù)
class Person {
  String name;

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

  Person(this.name);
}

類的繼承

  • Dart中同樣支持類的繼承, 繼承使用extends關(guān)鍵字缸濒,子類中使用super來(lái)訪問(wèn)父類
  • 父類中除了構(gòu)造方法外, 所有的成員變量和方法都會(huì)被繼承
  • 子類可以擁有自己的成員變量, 并且可以對(duì)父類的方法進(jìn)行重寫(xiě)
  • 子類中可以調(diào)用父類的構(gòu)造方法,對(duì)某些屬性進(jìn)行初始化:
    • 子類的構(gòu)造方法在執(zhí)行前,將隱含調(diào)用父類的無(wú)參默認(rèn)構(gòu)造方法(沒(méi)有參數(shù)且與類同名的構(gòu)造方法)
    • 如果父類沒(méi)有無(wú)參默認(rèn)構(gòu)造方法庇配,則子類的構(gòu)造方法必須在初始化列表中通過(guò)super顯式調(diào)用父類的某個(gè)構(gòu)造方法
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, 看個(gè)人需求是否調(diào)用
    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中沒(méi)有具體實(shí)現(xiàn)的方法(沒(méi)有方法體)斩跌,就是抽象方法
  • 抽象方法,必須存在于抽象類中, 抽象類不能實(shí)例化
  • 抽象類中的抽象方法必須被子類實(shí)現(xiàn), 抽象類中的已經(jīng)被實(shí)現(xiàn)方法, 可以不被子類重寫(xiě)
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) {
  // 實(shí)例化Size會(huì)報(bào)錯(cuò): Abstract classes can't be instantiated
  // var size = Size(20, 2);
  var area = Area(10, 2);
  area.getArea();
  print(area.getArea());
}

類成員和方法

Dart中我們使用static關(guān)鍵字來(lái)定義類成員和類方法

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

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


class Person {

  String firstName = 'top';
  // 不能使用this調(diào)用靜態(tài)屬性
  static String lastName = 'titanjun';


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

  // 靜態(tài)方法
  static void show() {
    print('https://www.$lastName.top');
  }
}

多繼承

Dart中只有單繼承, 是不支持多繼承的, 但是我們卻可以通過(guò)其他方式間接實(shí)現(xiàn)多繼承問(wèn)題

隱式接口

  • Dart中的接口比較特殊, 沒(méi)有一個(gè)專門的關(guān)鍵字來(lái)聲明接口, 默認(rèn)情況下所有的類都是隱式接口
  • 默認(rèn)情況下捞慌,定義的每個(gè)類都相當(dāng)于默認(rèn)也聲明了一個(gè)接口耀鸦,可以由其他的類來(lái)實(shí)現(xiàn)
  • Dart開(kāi)發(fā)中,我們通常將用于給別人實(shí)現(xiàn)的類聲明為抽象類
  • 當(dāng)將一個(gè)類能夠做接口使用時(shí), 那么實(shí)現(xiàn)這個(gè)接口的類, 必須實(shí)現(xiàn)這個(gè)接口中的所有方法
  • Dart中通過(guò)implements來(lái)實(shí)現(xiàn)多繼承問(wèn)題, 但是必須實(shí)現(xiàn)這個(gè)接口中的所有方法, 而且在方法的實(shí)現(xiàn)中不能調(diào)用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() {
    // 這里不能調(diào)用super方法
    // super.run(); 
    print("run");
  }
}

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

Mixin混入

  • 在通過(guò)implements實(shí)現(xiàn)某個(gè)類時(shí)啸澡,類中所有的方法都必須被重新實(shí)現(xiàn)(無(wú)論這個(gè)類原來(lái)是否已經(jīng)實(shí)現(xiàn)過(guò)該方法)
  • 但是某些情況下袖订,一個(gè)類可能希望直接復(fù)用之前類的原有實(shí)現(xiàn)方案
  • Dart提供了另外一種方案: Mixin混入的方式
    • 除了可以通過(guò)class定義類之外,也可以通過(guò)mixin關(guān)鍵字來(lái)定義一個(gè)類嗅虏。
    • 只是通過(guò)mixin定義的類用于被其他類混入使用洛姑,通過(guò)with關(guān)鍵字來(lái)進(jìn)行混入
mixin Runner {
  run() {
    print('在奔跑');
  }
}

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

// 這里可以對(duì)原方法不做任何實(shí)現(xiàn)
class Bird with Runner, Flyer {  }

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

參考資料


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市皮服,隨后出現(xiàn)的幾起案子楞艾,更是在濱河造成了極大的恐慌,老刑警劉巖龄广,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硫眯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜀细,警方通過(guò)查閱死者的電腦和手機(jī)舟铜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奠衔,“玉大人谆刨,你說(shuō)我怎么就攤上這事」榻铮” “怎么了痊夭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)脏里。 經(jīng)常有香客問(wèn)我她我,道長(zhǎng),這世上最難降的妖魔是什么迫横? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任番舆,我火速辦了婚禮聂儒,結(jié)果婚禮上智绸,老公的妹妹穿的比我還像新娘适刀。我一直安慰自己削锰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布沸伏。 她就那樣靜靜地躺著莫湘,像睡著了一般变逃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吗氏,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天芽偏,我揣著相機(jī)與錄音,去河邊找鬼弦讽。 笑死污尉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坦袍。 我是一名探鬼主播十厢,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼等太,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼捂齐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缩抡,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奠宜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后瞻想,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體压真,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蘑险,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滴肿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佃迄,死狀恐怖泼差,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呵俏,我是刑警寧澤堆缘,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站普碎,受9級(jí)特大地震影響吼肥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜麻车,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一缀皱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧动猬,春花似錦啤斗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)燃逻。三九已至,卻和暖如春臂痕,著一層夾襖步出監(jiān)牢的瞬間伯襟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工握童, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姆怪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓澡绩,卻偏偏與公主長(zhǎng)得像稽揭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肥卡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容