- 原文博客地址: 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í)
Dart
和Flutter
- 這篇
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í)行
{ }
之前, 必須保證name
和age
被初始化了 - 而且
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
中也存在setter
和getter
方法, 用于監(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);
}
- 上面
setName
和getName
是自定義的, 你也可以命名為setterName
和getterName
等 - 還有就是上述兩個(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)行混入
- 除了可以通過(guò)
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();
}
參考資料
- Flutter之搞定Dart二: coderwhy老師的公眾號(hào)知識(shí)
- Dart官網(wǎng)語(yǔ)法介紹-英文版
- Dart語(yǔ)言中文社區(qū)