?方法、異常與類(lèi)
?方法 ?一等?方法對(duì)象
可選命名參數(shù)
可選位置參數(shù)
默認(rèn)參數(shù)值
匿匿名?方法
異常 類(lèi)
構(gòu)造函數(shù)
命名構(gòu)造函數(shù)
初始化列列表
重定向構(gòu)造函數(shù)
常量量構(gòu)造函數(shù)
?廠(chǎng)構(gòu)造函數(shù)
Getters 和 Setters
可覆寫(xiě)的操作符 抽象類(lèi)
接?口 可調(diào)?用的類(lèi)
混合mixins
方法
int add(int i,int j) {
return i + j;
}
//也可以選擇忽略類(lèi)型(不推薦)
add( i, j) {
return i + j;
}
//對(duì)于只有一個(gè)表達(dá)式的方法,可以選擇使用縮寫(xiě)語(yǔ)法來(lái)定義:
add(i, j) => i + j;
//在箭頭 (=>) 和分號(hào) (;) 之間只能使用一個(gè) 表達(dá)式
一等方法對(duì)象
? Dart 是一個(gè)真正的面向?qū)ο笳Z(yǔ)言,方法也是對(duì)象并且具有一種 類(lèi)型 Function。 這意味著,方法可以賦值給變量,也可以當(dāng)做其他方法的參數(shù)??梢园逊椒ó?dāng)做參數(shù)調(diào)用另外一個(gè)方法
var list = [1,2,3];
//將 print 方法 作為參數(shù)傳遞給forEach
list.forEach(print);
//可以將方法賦值給一個(gè)變量 類(lèi)型為Funcation
var p = print;
list.forEach(p);
? 在Java中如果需要能夠通知調(diào)用者或者其他地方方法執(zhí)行過(guò)程的各種情況,可能需要指定一個(gè)接口,比如View的onClickListener。而在Dart中,我們可以直接指定一個(gè)回調(diào)方法給調(diào)用的方法,由調(diào)用的方法在合適的時(shí)機(jī)執(zhí)行這個(gè)回調(diào)。
void setListener(Function listener){
listener("Success");
}
//或者
void setListener(void listener(String result)){
listener("Success");
}
//兩種方式,第一種調(diào)用者根本不確定 回調(diào)函數(shù)的返回值、參數(shù)是些什么
//第二中則需要寫(xiě)這么一大段 太麻煩了。
//第三種:類(lèi)型定義 將返回值為voide,參數(shù)為一個(gè)String的方法定義為一個(gè)類(lèi)型。
typedef void Listener(String result);
void setListener(Listener listener){
listener("Success");
}
方法可以有兩種類(lèi)型的參數(shù):必需的和可選的。 必需的參數(shù)需要在參數(shù)列表前面, 后面再定義可選參數(shù)。
可選命名參數(shù)
? 把方法的參數(shù)放到 {} 中就變成可選 命名參數(shù)
int add({int i, int j}) {
if(i == null || j == null){
return 0;
}
return i + j;
}
? 調(diào)用方法的時(shí)候,可以使用這種形式 paramName: value 來(lái)指定命名參數(shù)。例如:
//無(wú)必須參數(shù)
add()
//選擇傳遞參數(shù)
add(i:1)
//位置無(wú)關(guān)
add(i:1, j:2)
add(j:1, i:2)
可選位置參數(shù)
? 把方法的參數(shù)放到 [] 中就變成可選 位置參數(shù),傳值時(shí)按照參數(shù)位置順序傳遞
int add([int i, int j]) {
if(i == null || j == null){
return 0;
}
return i + j;
}
// 1賦值給i
add(1);
// 按照順序賦值
add(1,2);
默認(rèn)參數(shù)值
? 在定義方法的時(shí)候,可選參數(shù)可以使用 = 來(lái)定義可選參數(shù)的默認(rèn)值。
int add([int i = 1, int j = 2]) => i + j;
int add({int i = 1, int j = 2}) => i + j;
匿名方法
? 沒(méi)有名字的方法,稱(chēng)之為匿名方法,也可以稱(chēng)之為 lambda 或者 closure 閉包。匿名方法的聲明為:
([Type] param1, …) {
codeBlock;
};
? 如:
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
print(list[i]);
});
異常
? 和 Java 不同的是,所有的 Dart 異常是非檢查異常。 方法不一定聲明了他們所拋出的異常, 并且不要求你捕獲任何異常。
? Dart 提供了 Exception和Error 類(lèi)型, 以及一些子類(lèi)型。你還 可以定義自己的異常類(lèi)型。但是, Dart 代碼可以 拋出任何非 null 對(duì)象為異常,不僅僅是實(shí)現(xiàn)了 Exception 或者Error 的對(duì)象。
throw new Exception('這是一個(gè)異常');
throw '這是一個(gè)異常';
throw 123;
? 與Java不同之處在于捕獲異常部分,Dart中捕獲異常同樣是使用catch語(yǔ)句,但是Dart中的catch無(wú)法指定異常類(lèi)型。需要結(jié)合on來(lái)使用,基本語(yǔ)法如下:
try {
throw 123;
} on int catch(e){
//使用 on 指定捕獲int類(lèi)型的異常對(duì)象
} catch(e,s){
//函數(shù) catch() 可以帶有一個(gè)或者兩個(gè)參數(shù), 第一個(gè)參數(shù)為拋出的異常對(duì)象, 第二個(gè)為堆棧信息 ( StackTrace 對(duì)象)
rethrow; //使用 `rethrow` 關(guān)鍵字可以 把捕獲的異常給 重新拋出
} finally{
}
類(lèi)
? Dart 是一個(gè)面向?qū)ο缶幊陶Z(yǔ)言。 每個(gè)對(duì)象都是一個(gè)類(lèi)的實(shí)例,所有的類(lèi)都繼承于 Object。
//每個(gè)實(shí)例變量都會(huì)自動(dòng)生成一個(gè) getter 方法(隱含的)。 非final 實(shí)例變量還會(huì)自動(dòng)生成一個(gè) setter 方法。
class Point {
num x;
num y;
}
構(gòu)造函數(shù)
? 由于把構(gòu)造函數(shù)參數(shù)賦值給實(shí)例變量的場(chǎng)景太常見(jiàn)了, Dart 提供了一個(gè)語(yǔ)法糖來(lái)簡(jiǎn)化這個(gè)操作:
class Point {
num x;
num y;
Point(this.x, this.y);
}
命名構(gòu)造函數(shù)
? Dart 并不支持構(gòu)造函數(shù)的重載,而采用了命名構(gòu)造函數(shù)為一個(gè)類(lèi)實(shí)現(xiàn)多個(gè)構(gòu)造函數(shù):
class Point {
num x;
num y;
Point(this.x, this.y);
Point(this.y);///錯(cuò)誤,不允許重載
//命名構(gòu)造函數(shù)
Point.y(this.y) {
x = 0;
}
}
//使用
var p = Point.y(0);
初始化列表
? 在構(gòu)造函數(shù)函數(shù)體執(zhí)行之前會(huì)首先執(zhí)行初始化列表,非常適合用來(lái)設(shè)置 final 變量的值。
class Point {
num x;
num y;
Point(this.x, this.y);
//命名構(gòu)造函數(shù)
Point.y(this.y) {
x = 0;
}
Point.fromMap(Map map)
: x = map['x'], // : 和c++一樣,初始化列表
y = map['y'];
Point.x(int i)
: x = i,
y = 0;
}
重定向構(gòu)造函數(shù)
? 有時(shí)候一個(gè)構(gòu)造函數(shù)會(huì)調(diào)動(dòng)類(lèi)中的其他構(gòu)造函數(shù)(在Java中就是 this(...))。 一個(gè)重定向構(gòu)造函數(shù)是沒(méi)有代碼的,在構(gòu)造函數(shù)聲明后,使用 冒號(hào)調(diào)用其他構(gòu)造函數(shù)。
class Point {
num x;
num y;
Point(this.x, this.y);
Point.xy(int x,int y):this(x,y); ///調(diào)用上面的構(gòu)造函數(shù)
}
常量構(gòu)造函數(shù)
? 如果你的類(lèi)提供一個(gè)狀態(tài)不變的對(duì)象,你可以把這些對(duì)象 定義為編譯時(shí)常量。要實(shí)現(xiàn)這個(gè)功能,需要定義一個(gè) const 構(gòu)造函數(shù), 并且聲明所有類(lèi)的變量為 final。
class ImmutablePoint {
final num x;
final num y;
//常量構(gòu)造函數(shù)
const ImmutablePoint(this.x, this.y);
}
void main(){
//編譯器常量
var p1 = const ImmutablePoint(0,0);
var p2 = const ImmutablePoint(0,0);
print(p1 == p2); // true
}
工廠(chǎng)構(gòu)造函數(shù)
? 當(dāng)實(shí)現(xiàn)一個(gè)使用factory 關(guān)鍵詞修飾的構(gòu)造函數(shù)時(shí),這個(gè)構(gòu)造函數(shù)不必創(chuàng)建類(lèi)的新實(shí)例。例如,一個(gè)工廠(chǎng)構(gòu)造函數(shù) 可能從緩存中獲取一個(gè)實(shí)例并返回,或者 返回一個(gè)子類(lèi)型的實(shí)例。(工廠(chǎng)構(gòu)造函數(shù)無(wú)法訪(fǎng)問(wèn) this)
class Logger {
final String name;
//從緩存獲取對(duì)象
static final Map _cache = {};
//工廠(chǎng)構(gòu)造函數(shù),無(wú)法使用this變量
factory Logger(String name) {
if (_cache.containsKey(name)) {
//工廠(chǎng)構(gòu)造函數(shù)需要返回 Logger 實(shí)例對(duì)象
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
//以 _ 開(kāi)頭的函數(shù)、變量無(wú)法在庫(kù)外使用
Logger._internal(this.name);
}
? 借助工廠(chǎng)構(gòu)造函數(shù)能夠?qū)崿F(xiàn)單例:
//使用工廠(chǎng)構(gòu)造實(shí)現(xiàn)單例
class Manager {
static Manager _instance;
//和static是一樣的, 區(qū)別是factory畢竟是構(gòu)造函數(shù),需要返回一個(gè)實(shí)例,而static是靜態(tài)方法。
factory Manager.getInstance() {
if (_instance == null) {
_instance = new Manager._internal();
}
return _instance;
}
// static Manager getInstance() {
// if (_instance == null) {
// _instance = new Manager._internal();
// }
// return _instance;
// }
Manager._internal();
}
Getters 和 Setters
? Dart中每個(gè)實(shí)例變量都隱含的具有一個(gè) getter, 如果變量不是 final 的則還有一個(gè) setter??梢酝ㄟ^(guò)實(shí)現(xiàn) getter 和 setter 來(lái)創(chuàng)建新的屬性, 使用 get 和 set 關(guān)鍵字定義 getter 和 setter:
class Rect {
num left;
num top;
num width;
num height;
Rect(this.left, this.top, this.width, this.height);
//使用 get定義了一個(gè) right 屬性
num get right => left + width;
set right(num value) => left = value - width;
}
void main() {
var rect = Rect(0, 0, 10, 10);
print(rect.right); //10
rect.right = 15;
print(rect.left); //5
}
需要注意的是,在get與set中使用自身會(huì)導(dǎo)致Stack Overflow
可覆寫(xiě)的操作符
? 把已經(jīng)定義的、有一定功能的操作符進(jìn)行重新定義??梢灾匦露x的操作符有:
| < | + | | | [] |
| ---- | ---- | ---- | ----- |
| > | / | ^ | []= |
| <= | ~/ | & | ~ |
| >= | * | << | == |
| – | % | >> | |
? 比如:List就重寫(xiě)了 []。
class Point {
int x;
int y;
//返回值 參數(shù)隨你定義
Point operator +(Point point) {
return Point(x + point.x, y + point.y);
}
Point(this.x, this.y);
}
var p1 = Point(1, 1);
var p2 = p1 + Point(2, 2);
print(p2.x); ///3
print(p2.y); ///3
抽象類(lèi)
? 使用 abstract 修飾符定義一個(gè)抽象類(lèi)。抽象類(lèi)中允許出現(xiàn)無(wú)方法體的方法
abstract class Parent {
String name;
void printName(); //抽象方法,不需要在方法前聲明 abstract
}
? 抽象類(lèi)不能被實(shí)例化,除非定義工廠(chǎng)方法并返回子類(lèi)。
abstract class Parent {
String name;
//默認(rèn)構(gòu)造方法
Parent(this.name);
//工廠(chǎng)方法返回Child實(shí)例
factory Parent.test(String name){
return new Child(name);
}
void printName();
}
// extends 繼承抽象類(lèi)
class Child extends Parent{
Child(String name) : super(name);
@override
void printName() {
print(name);
}
}
void main() {
var p = Parent.test("Lance");
print(p.runtimeType); //輸出實(shí)際類(lèi)型 Child
p.printName();
}
接口
? 與Java不同,Dart中沒(méi)有interface關(guān)鍵字,Dart中每個(gè)類(lèi)都隱式的定義了一個(gè)包含所有實(shí)例成員的接口, 并且這個(gè)類(lèi)實(shí)現(xiàn)了這個(gè)接口。如果你想 創(chuàng)建類(lèi) A 來(lái)支持 類(lèi) B 的 方法,而不想繼承 B 的實(shí)現(xiàn), 則類(lèi) A 應(yīng)該實(shí)現(xiàn) B 的接口。
class Listener{
void onComplete(){}
void onFailure(){}
}
class MyListsner implements Listener{
MyListsner(){
}
@override
void onComplete() {
}
@override
void onFailure() {
}
}
與繼承的區(qū)別在于:
1、單繼承,多實(shí)現(xiàn)。
2、繼承可以有選擇的重寫(xiě)父類(lèi)方法并且可以使用super,實(shí)現(xiàn)強(qiáng)制重新定義接口所有成員。
可調(diào)用的類(lèi)
? 如果 Dart 類(lèi)實(shí)現(xiàn)了 call() 函數(shù)則 可以當(dāng)做方法來(lái)調(diào)用。
class Closure {
call(String a, String b) => '$a $b!';
}
main() {
var c = new Closure();
var out = c("Hello","Dart");
print(out);
}
混合mixins
? Mixins 是一種在多類(lèi)繼承中重用 一個(gè)類(lèi)代碼的方法。它的基本形式如下:
//被mixin(混入)的類(lèi)不能有構(gòu)造函數(shù)
class A {
void a(){}
}
class B{
void b(){}
}
class C with A,B{
void c(){}
}
with后面跟著需要混入的類(lèi),被mixin(混入)的類(lèi)不能有構(gòu)造函數(shù)?,F(xiàn)在的 C擁有了三個(gè)方法(a、b與c)。假設(shè)A與B 存在相同的方法,以最右側(cè)的混入類(lèi)為主,比如:
class A {
String getMessage() => 'A';
}
class B {
String getMessage() => 'B';
}
//
class AB with A, B {}
class BA with B, A {}
void printMessage(obj) => print(obj.getMessage());
void main() {
printMessage(AB()); //輸出 B
printMessage(BA()); //輸出 A
}
繼承與mixins是兼容的
class A {
String getMessage() => 'A';
}
class B {
String getMessage() => 'B';
}
class P{
String getMessage() => 'P';
}
class AB extends P with A, B {}
class BA extends P with B, A {}
//可以簡(jiǎn)寫(xiě)成:
//class AB = P with A, B;
//class BA = P with B, A;
void printMessage(obj) => print(obj.getMessage());
void main() {
printMessage(AB()); //輸出 B
printMessage(BA()); //輸出 A
}
? mixins彌補(bǔ)了接口和繼承的不足,繼承只能單繼承,而接口無(wú)法復(fù)用實(shí)現(xiàn),mixins卻可以多混入并且能利用到混入類(lèi)的具體實(shí)現(xiàn):
abstract class Swimming{
void swimming(){
print("游泳");
}
}
abstract class Jump{
void jump(){
print("跳躍");
}
}
//只能單繼承,如果需要Jump,只能以implements的形式
class Lance extends Swimming implements Jump{
//實(shí)現(xiàn)接口
void jump(){
print("跳躍");
}
}
//但是實(shí)際上,我們經(jīng)常不需要重新實(shí)現(xiàn)Jump方法,復(fù)用Jump所實(shí)現(xiàn)的jump方法就可以了
//這時(shí)使用混合能夠更加方便
class Lance1 with Swimming, Jump {}