類(Class)是面向對象程序設計,實現信息封裝的基礎。類是一種用戶定義的類型。每一個類包含數聽說明和一組操做數據或傳遞消息的函數。類的實例稱爲對象。java
Dart的類與其它語言都有很大的區別,好比在dart的類中能夠有無數個構造函數,能夠重寫類中的操做符,有默認的構造函數,因爲dart沒有接口,因此dart的類也是接口,所以你能夠將類做爲接口來從新實現。程序員
Dart是一門使用類和單繼承的面嚮對象語言全部的對象都是類的實例,而且全部的類都是Object
的子類。
swift
class
關鍵字類首字母必須大寫緩存
new
關鍵字和構造函數
來建立對象class Person { //未定義父類的時候,默認繼承自Object num x; num y; num z; } void main(List<String> args){ var person = new Person();//調用默認的構造函數 person.x = 10; //使用點()引用實例變量或方法 person.y = 11; person?.z = 12; //若是p不爲空,設置它的變量y的值爲4 print(person.x); print(person.y); print(person.z); }.
結果:ide
10 11 12
.
)引用實例變量或方法; ?.
來確認前操做數不爲空, 經常使用來替代.
, 避免左邊操做數爲null引起異常void main(){ var point = new Point(); point.x = 4; //使用點()引用實例變量或方法
point?.y = 5;//若是p不爲空,設置它的變量y的值爲5
print(point.x); print(point.y);
} .
class Point {
int x; // null
int y; // null
int z = 0; // 0
}
class Person { int x; int y; } void main(List<String> args){ var person = new Person(); }
void main(){ var point = new Point(4, 5); }
class Point { int x; int y;
//本身定義的類名構造函數
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
在Dart中構造函數的名稱能夠是類名 ClassName
或者類名和標識符 ClassName.identifier
。 其中構造函數名稱是「ClassName」的函數叫「類名構造函數」;構造函數名稱是「ClassName.identifier」的函數叫「命名構造函數」。
(1)類名構造函數 (ClassName)函數
import 'dart:math'; class Point { int y; int x; // 類名構造函數 Point(num x, num y) { this.x = x; this.y = y; } // ..... }
在構造函數裏初始化成員屬性是很常見的事情,所以Dart開發了新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成post
class Point { num x, y; // 注意x,y的賦值會在構造函數執行以前完成. Point(this.x, this.y); }
(2)命名構造函數(ClassName.identifie)
使用命名構造函數能夠爲類提供多個構造函數,按官方的說法就是提供額外的清晰度this
class Point { num x, y; Point(this.x, this.y); // 命名構造函數 Point.origin() { x = 0; y = 0; } }
調用命名構造函數spa
在命名構造函數裏也能夠用新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成 設計
(2)命名構造函數(ClassName.identifie)
使用命名構造函數能夠爲類提供多個構造函數,按官方的說法就是提供額外的清晰度
class Point { num x, y; Point(this.x, this.y); // 命名構造函數 Point.origin() { x = 0; y = 0; } }
調用命名構造函數
main(List<String> args) { // 調用命名構造函數 Point point1 = Point.origin(); }
在命名構造函數裏也能夠用新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成
class Point { num x, y; //類名構造函數 Point(this.x, this.y); // 命名構造函數 Point.origin(this.x,this.y); }
void main(List<String> args){ var point = new Point.Orgin(1,2); print(point.x); print(point.y); }
(3)默認構造函數(前面我咱們已經說了,咱們放在這裏再提一下,方便區分)
若是類中沒有聲明構造函數,Dart會提供一個默認的構造函數。這個默認的構造函數會調用父類的默認構造函數,而且該構造函數是沒有參數的。
class Person { int x; int y; } void main(List<String> args){ var person = new Person(); }
Dart的第一個版本實例化對象須要new關鍵字,但在Dart 2以後就去掉了new關鍵字
main(List<String> args) { // 調用類名構造函數 Point point1 = Point(3,4); print(point1.x); }
調用父類非默認的構造函數(類比下面的重定向理解記憶)
在默認狀況下,子類能夠調用父類的未命名,無參數的構造函數即默認構造函數。父類的構造函數會在子類的構造函數以前開始調用,若是子類中存在須要初始化的成員屬性,則能夠先初始化子類成員屬性,再調用父類的構造函數,執行過程以下
若是父類中沒有默認的構造函數,你必須手動調用父類的構造函數,在子類的構造函數體以前經過 :
指定調用父類構造函數,示例以下
// Person類中沒有一個無參數,未命名的構造函數 class Person { String firstName; // 命名構造函數 Person.fromJson(Map data) { print('in Person'); } } class Employee extends Person { // 你必須調用父類的super.fromJson(data). Employee.fromJson(Map data) : super.fromJson(data) { print('in Employee'); } } main() { var emp = new Employee.fromJson({});
}
重定向構造函數 (在這裏 :有從新指向的含義)
有時構造函數的惟一目的是重定向到同一類中的另外一個構造函數。重定向構造函數的主體爲空,構造函數調用出如今冒號( :
)以後 。 大意就是在建立類時,我定義一個命名構造函數,可是這個構造函式的主體我不實現。直接經過:另一個構造函數。實現對外界傳入的參數接收並賦值給內部的變量。
class Point { num x, y; //類名構造函數 Point(this.x, this.y); // 命名構造函數 Point.order(this.x,this.y); Point.origin(num a,num b):this.order(a,b); //重定向構造函數, origin構造函數將外界的傳值,指向給了order構造函數。 } void main(List<String> args){ var point = new Point.origin(1,2); print(point.x); print(point.y); }
const
構造函數,且保證全部的對象變量都是final。class ImmutablePoint { static final ImmutablePoint origin = const ImmutablePoint(0, 0); final num x, y; const ImmutablePoint(this.x, this.y); }
工廠構造函數
在實現一個構造函數時使用factory
關鍵字,該構造函數並不老是建立其類的新實例。例如,工廠構造函數可能會從緩存中返回實例,也可能會返回子類型的實例。
class Logger { final String name; bool mute = false; // _cache是私有變量 //_在名稱前,表示該變量爲私有 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沒有訪問權限。
像調用任何其餘構造函數同樣調用工廠構造函數:
var logger = Logger('UI'); logger.log('Button clicked');
方法是爲對象提供行爲的函數。
get
和 set
關鍵詞重寫對象的默認行爲。class Rectangle { num left, top, width, height; //類名構造函數 Rectangle(this.left, this.top, this.width, this.height); // 重寫right屬性(類比oc記憶,這裏多了一個set和get的關鍵字) num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; } void main() { var rect = Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
實例方法
在對象的實例方法有權限獲取對象變量和this
,在接下來的例子裏distanceTo
就是一個對象方法:
import 'dart:math'; class Point { num x, y; Point(this.x, this.y); num distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } }
抽象方法
(1) 實例的getter和setter方法就是抽象的方法,定義一個接口,但將其實現留給其餘類。
(2)抽象方法使用分號 ; 而不是方法體
(3)抽象方法只存在於抽象類中。
abstract class Doer { //...定義實例變量和方法... //定義一個抽象方法 void doSomething(); } class EffectiveDoer extends Doer { void doSomething() { //...實現一個抽象方法... } }
抽象類
使用 abstract
修改器能夠定義一個抽象類。抽象類是不能被實例化的,但對於定義接口是很是有用的,若是你想實例化抽象類,你必須實現抽象類,才能被實例化
// 此對象愛你過是抽象類,所以不能被實例化 abstract class AbstractContainer { // 定義構造函數、字段、方法... void updateChildren(); // 抽象方法 }
隱式的接口
每一個類都是都是隱式的接口,包括類的方法和屬性。若是你想建立一個類A不繼承B的實現,能夠實現B的接口來建立類A。一個類容許經過implements
關鍵詞能夠實現多個接口
// 每一個類都是一個隱式的接口,因此Person類也是個接口,包括成員屬性和方法. class Person { // 可在接口中實現, 但僅對這個庫可見. final _name; // 構造函數不可以被接口實現 Person(this._name); // 可在接口中實現. String greet(String who) => 'Hello, $who. I am $_name.'; } // 實現Person接口. class Impostor implements Person { get _name => ''; String greet(String who) => 'Hi $who. Do you know who I am?'; } String greetBob(Person person) => person.greet('Bob'); void main() { print(greetBob(Person('Kathy'))); print(greetBob(Impostor())); }
實現多個接口
class Point implements Comparable, Location {...}
class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } } class SmartTelevision extends Television { void turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); }
重寫成員
可使用 @override
關鍵字,子類能夠重寫實例的方法,getters和setters
class SmartTelevision extends Television { @override void turnOn() {...} // ··· }
重寫操做符
你能夠重寫如下表中顯示的運算符。例如,若是定義Vecor類,則能夠定義+
方法來添加兩個vectors。
注意:
你可能已經注意到了!=
不是可重寫的運算符。表達式E1!= E2
只是語法上的糖!( E1 = = E2 )
< + | [] > / ^ []=
<= ~/ & ~
>= * << == – %
能夠重寫操做符,下面是重寫加減操做符的示例
若是你須要重寫 ==
操做符,請參考操做符教程
noSuchMethod()
當用戶調用你定義的類中不存在的屬性與方法時,能夠作出一些響應,經過重寫noSuchMethod()
class A {
@override
void noSuchMethod(Invocation invocation) { print('You tried to use a non-existent member: ' + '${invocation.memberName}'); } }
使用static
關鍵字實現類範圍的變量和方法。
class Queue { static const initialCapacity = 16; // ··· } void main() { assert(Queue.initialCapacity == 16); }
靜態方法(類方法)不能在實例操做,所以它沒有訪問this的權限。
import 'dart:math'; class Point { num x, y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); }
注意:
(1)爲了經常使用或普遍使用的實用程序和功能,考慮使用頂層函數,而不是靜態方法。
import 'dart:math'; class Point { num x, y; Point(this.x, this.y); } // 頂級函數 num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); }
(2)可使用靜態方法做爲編譯時常量。例如,能夠將靜態方法做爲參數傳遞給常量構造函數。
九,訪問控制
main(List<String> args) { Dog d = new Dog('Duffy', 5); print(d.name); //This will throw error } class Dog { String _name; //私有的變量 int age; //公有變量
//類名構造函數 Dog(this.name, this.age);
//setter && getter 方法 String get respectedName { return 'Mr.$name'; }
set respectedName(String newName) { name = newName; }
//命名構造函數 Dog.newBorn() { name = 'Doggy'; age = 0; }
//公有實例方法 bark() { print('Bow Wow'); }
//私有的實例方法 _hiddenMethod() { print('I can only be called internally!'); } }
十,枚舉類型
枚舉類型,一般稱爲枚舉,是一種特殊類型的類,用於表示固定數量的常量值。
使用枚舉
使用enum
關鍵詞來聲明一個枚舉類型
enum Color { red, green, blue }
枚舉中的每一個值都有一個index
索引,它返回枚舉聲明中值的從零開始的位置。例如,第一個值具備索引0,第二個值具備索引1。
assert(Color.red.index == 0); assert(Color.green.index == 1); assert(Color.blue.index == 2);
若要獲取枚舉中全部值的列表,請使用枚舉的values
常量。
List<Color> colors = Color.values; assert(colors[2] == Color.blue);
你能夠在switch語句中使用枚舉,若是不處理枚舉的全部值,將會收到警告:
var aColor = Color.blue; switch (aColor) { case Color.red: print('Red as roses!'); break; case Color.green: print('Green as grass!'); break; default: // 沒有default,將會報錯 print(aColor); // 'Color.blue' }
枚舉類型有如下限制:
main(List<String> args) { DataHolder<String> dataHolder = new DataHolder('Some data'); print(dataHolder.getData()); dataHolder.setData('New Data'); print(dataHolder.getData()); } class DataHolder<T> { T data; DataHolder(this.data); getData() { return data; } setData(data) { this.data = data; } }