Dart語法系列(三):函數(shù)與異常

一笼踩、 Functions (函數(shù))

Dart 是一個真正的面向?qū)ο笳Z言,函數(shù)也是一個對象并且具有其類型:Function。這意味著烛占,方法可以賦值給變量,也可以當(dāng)做其他方法的參數(shù)。您還可以像調(diào)用函數(shù)一樣調(diào)用Dart類的實例忆家。詳情請參考 Callable classes犹菇。

Dart是不支持函數(shù)的重載的****,一個類中不允許存在同名的函數(shù)出現(xiàn)芽卿,這點與C語言一樣揭芍。雖然不支持函數(shù)重載但是它支持可選參數(shù)的使用

下面是定義方法的示例:

bool isNoble(int atomicNumber) {
  return atomicNumber != null;
}

Dart中函數(shù)的返回值類型 和 參數(shù)類型都可以省略****卸例,但是Effective Dart 中推薦最好不要省略

isNoble(atomicNumber) {
  return atomicNumber != null;
}

對于只包含一個表達式的函數(shù)称杨,可以使用簡寫語法:(return必須省略)

bool isNoble(int atomicNumber) => atomicNumber != null; //return必須省略

//同樣可以省略類型,下面等價
isNoble(atomicNumber) => atomicNumber != null;

這個 => expr 語法是 { return expr; } 形式的縮寫筷转。=> 形式 有時候也稱之為 箭頭語法( arrow syntax)姑原。

注意: 在箭頭 => 和冒號 ; 之間只能使用一個表達式(expression) ,而不能使用語句(statement)呜舒。 例如:你不能使用 if statement锭汛,但是可以 使用條件表達式 conditional expression

方法可以有兩種類型的參數(shù):必需的和可選的袭蝗。命名可選參數(shù)也可以標(biāo)記為@required唤殴。

1.1、可選參數(shù)

可選參數(shù)可以是基于命名參數(shù)或者基于位置的參數(shù)呻袭,但是這兩種參數(shù)不能同時當(dāng)做可選參數(shù)眨八。

如果參數(shù)列表中同時存在可選參數(shù) 和 必選參數(shù),必選參數(shù)必需放在可選參數(shù)前面左电!

Optional named parameters(可選命名參數(shù))

在定義方法的時候廉侧,使用 {param1, param2, …} 的形式來指定命名可選參數(shù)。

在調(diào)用方法的時候篓足,你可以使用這種形式 paramName: value 來指定命名參數(shù)段誊,而不能直接傳值:

//函數(shù)定義,注意有{} 
enableFlags({bool bold, bool hidden}) {
  // ...
}

//函數(shù)調(diào)用
enableFlags(hidden: false,bold: true);//OK 
enableFlags(bold: true);//OK
enableFlags();//OK
//enableFlags(false, true);//編譯錯誤 

{}表示可選參數(shù)栈拖,當(dāng)然你也可以按需傳入可選參數(shù)连舍,但是必選參數(shù)必選要傳遞

//a是必選參數(shù), {}中的是可選參數(shù)
//void enableFlags({bool bold, bool hidden},int a) //錯誤定義涩哟,可選參數(shù)只能放在后面
void enableFlags(int a,{bool bold, bool hidden}) { 
  print(bold.toString()+" "+hidden.toString());
}

main() {
  enableFlags(3,bold: true); //true null
  enableFlags(3);  //null null
  //enableFlags(); 錯誤索赏。有必選參數(shù)a存在,不能不傳任何參數(shù)
}

注意贴彼,可選參數(shù)只能放參數(shù)列表的在最后潜腻。

Optional positional parameters(可選位置參數(shù))

把一些方法的參數(shù)放到 [ ] 中就變成可選 位置參數(shù)了,可選位置參數(shù)只能根據(jù)位置選擇傳入的參數(shù)器仗,而不能通過命名的方式融涣,其他的效果與可選命名參數(shù)一致:

//[]表示可選位置參數(shù)
void enableFlags(int a,[bool hidden, bool bold]) {
  print(bold.toString()+" "+hidden.toString());
}

main() {
  enableFlags(3,true,true); //true true
  enableFlags(3,true); //null true

}

Default parameter values(默認參數(shù)值)

在定義方法的時候童番,可以使用 = 來定義可選參數(shù)的默認值,只能用于可選參數(shù)(兩種可選參數(shù)[ ]和{}都支持)威鹿。 默認值只能是編譯時常量剃斧。 如果沒有提供默認值,則默認值為 null (Dart中萬物都是對象忽你,沒有基本類型之分)幼东。

void enableFlags(int a ,{bool hidden = true, bool bold }) {
  print(bold.toString()+" "+hidden.toString());
}


main() {
  enableFlags(3); //null true
  enableFlags(3,hidden: false); //null false
  enableFlags(3,hidden: false,bold: false); //false false
}

版本問題: 舊版本代碼可能需要使用一個冒號 (:) 而不是 = 來設(shè)置參數(shù)默認值。 原因在于 Dart SDK 1.21 之前的版本檀夹,命名參數(shù)只支持 :筋粗。 : 設(shè)置命名默認參數(shù)值在以后版本中將不能使用, 所以我們推薦你 使用 = 來設(shè)置默認值炸渡, 并 指定 Dart SDK 版本為 1.21 或者更高的版本娜亿。

還可以使用 list 或者 map 作為默認值。 下面的示例定義了一個方法 doStuff()蚌堵, 參數(shù)類型分別為list和map买决,并分別為其指定了默認值,由于默認值必須是編譯時常量吼畏,所以必須使用const修飾

void doStuff(
    {List<int> list = const [1, 2, 3], Map<String, String> gifts = const {
        'first': 'paper',
        'second': 'cotton',
        'third': 'leather'
      }}) 
{
  print('list:  $list');
  print('gifts: $gifts');
}


main() {
 doStuff();
}

1.2督赤、main函數(shù)

每個應(yīng)用都需要有個頂級的 main() 入口方法才能執(zhí)行。 main() 方法的返回值為 void 并且有個可選的 List<String> 參數(shù)泻蚊。

下面是一個 web 應(yīng)用的 main() 方法:

void main() {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}

下面是一個命令行應(yīng)用的 main() 方法躲舌,并且使用了方法參數(shù)作為輸入?yún)?shù):

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}

1.3、函數(shù)是一等公民(Functions as first-class objects)

函數(shù)可以作為參數(shù)來進行調(diào)用性雄,例如:

printElement(element) {
  print(element);
}

main() {
  var list = [1, 2, 3];
  list.forEach(printElement);  
}

輸出結(jié)果為:1没卸、2、3秒旋。 我們可以看看forEach的函數(shù)定義约计,直接聲明一個函數(shù)的聲明作為函數(shù)的參數(shù)

img

我們也可以為變量分配一個函數(shù),例如下面:(這個例子使用一個匿名函數(shù)迁筛。下一節(jié)將詳細介紹這些內(nèi)容)

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

1.4煤蚌、Anonymous functions(匿名函數(shù))

你有可以創(chuàng)建沒有函數(shù)名的函數(shù),稱之為匿名函數(shù)细卧,有時候也被稱為 lambda 或者 closure 閉包尉桩。 你可以把匿名方法賦值給一個變量, 然后你可以使用這個方法贪庙,比如添加到集合或者從集合中刪除魄健。您可以為變量分配一個匿名函數(shù),例如插勤,然后你可以使用這個方法,比如添加到集合或者從集合中刪除。

匿名函數(shù)看起來類似于命名函數(shù)—有0個或者多個參數(shù)农尖,在括號之間用逗號和可選類型標(biāo)注分隔析恋。后面的代碼塊包含函數(shù)的主體:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 

下面的示例定義了一個無類型參數(shù)item的匿名函數(shù)。list 中的每個元素都會調(diào)用這個函數(shù)來 打印出來盛卡,同時來計算了每個元素在 list 中的索引位置:

var list = ['apples', 'bananas', 'oranges'];
//1助隧、匿名函數(shù)允許直接賦予給一個變量
var a =  (item) {
  print('${list.indexOf(item)}: $item');
};
list.forEach(a);//a指向一個匿名函數(shù)

//2、也允許直接傳入一個匿名函數(shù),與上面的兩句效果是等價的
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

/*打印結(jié)果
0: apples
1: bananas
2: oranges
*/

如果函數(shù)只包含一個語句滑沧,可以使用箭頭語法縮寫

var list = ['apples', 'bananas', 'oranges'];
  
var a = (item) => print('${list.indexOf(item)}: $item');
list.forEach(a);

//與上面兩句等價
list.forEach((item) => print('${list.indexOf(item)}: $item'));

1.5并村、Lexical scope(靜態(tài)作用域)

Lexical Scope (also called Static Scope)。Dart 是靜態(tài)作用域語言滓技,變量的作用域在寫代碼的時候就確定過了哩牍。 基本上大括號里面定義的變量就只能在大括號里面訪問,和 Java 作用域類似令漂。

這里有一個嵌套函數(shù)的例子膝昆,每個作用域級別上都有變量:

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

nestedFunction() 可以訪問所有的變量, 包含頂級變量叠必。

1.6荚孵、Lexical closures(詞法閉包)

一個 閉包(匿名函數(shù)) 是一個函數(shù)對象不管該對象在何處被調(diào)用纬朝, 該對象都可以訪問其作用域內(nèi)的變量收叶。

在下面的示例中,makeAdder()捕獲變量addBy共苛。無論返回的函數(shù)到哪里判没,它都會記住addBy:

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;  //匿名函數(shù)作為Function類型的對象進行返回
}

void main() {
    // Create a function that adds 2.
    var add2 = makeAdder(2);

    // Create a function that adds 4.
    var add4 = makeAdder(4);

    assert(add2(3) == 5);
    assert(add4(3) == 7);
}

在上面的示例中我們可以看出:普通函數(shù),匿名函數(shù)都是一個Function類型的對象俄讹,并且可以作為返回值進行返回哆致。

例如下面例子,add2指向的就是test函數(shù)對象患膛,當(dāng)執(zhí)行add2()的時候?qū)嶋H上調(diào)用的是test()函數(shù):

test(){
  print("Hello World");
}
 
Function makeAdder(addBy) {//返回類型可以省略
  return test ;
}

main() {
  var add2 = makeAdder(2);
  add2(); //Hello World 
}

1.7摊阀、判斷函數(shù)相等

下面是測試頂級方法靜態(tài)函數(shù)實例函數(shù) 相等的示例:(下面都是執(zhí)行通過的)

foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = new A(); // Instance #1 of A
  var w = new A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}

上面的例子可以看出 同一個類的不同實例之間踪蹬,對于同一個方法來說其對象是不相等的胞此,既var v = A()var w = A() 中 v.bar 與 w.bar 方法對象是不相等的。

1.8跃捣、Return values(返回值)

所有函數(shù)都返回一個值漱牵。如果沒有指定返回值,則 默認把語句 return null疚漆,作為函數(shù)的最后一個語句執(zhí)行酣胀。

foo() {}

assert(foo() == null);

二刁赦、異常

代碼中可以出現(xiàn)異常和捕獲異常。異常表示一些 未知的錯誤情況闻镶。如果異常沒有捕獲甚脉, 則異常會拋出,導(dǎo)致 拋出異常的代碼終止執(zhí)行铆农。

和 Java 不同的是牺氨,所有的 Dart 異常是非檢查異常 unchecked exceptions(既不需要一定要用try ...catch處理)方法不聲明它們可能拋出哪些異常墩剖,也不要求您捕獲任何異常:

test(){//方法體也不用聲明
  throw new FormatException("Test one ");
}

void main() {
    test();//不需要try-catch
}

Dart 提供了 ExceptionError 類型猴凹, 以及一些子類型。你還 可以定義自己的異常類型岭皂。但是郊霎, Dart 代碼可以拋出任何非 null 對象為異常,不僅僅是實現(xiàn)了 Exception 或者 Error 的對象蒲障。( Dart拋出的異常類型不一定要是Exception和Error類型歹篓,任何非空類型都可以)。

2.1揉阎、throw

下面是拋出一個異常的示例:

throw new FormatException('Expected at least 1 section');//FormatException是Exception的子類

你也可以拋出任意對象:

throw 'Out of llamas!';

注意:在正式使用中通常拋出的是實現(xiàn)了Error或Exception類型的對象庄撮;

由于拋出異常是一個表達式,所以可以在 => 語句中使用毙籽,也可以在其他能使用表達式的地方拋出異常:

void distanceTo(Point other) => throw UnimplementedError();

2.2洞斯、Catch

與java一樣,Dart中可以使用 try-catch來捕獲異常坑赡,避免異常繼續(xù)傳遞(除非你重新拋出rethrow異常)烙如。不過與java不同的事,使用on ExceptionType來聲明捕獲具體的異常類型毅否。on ExceptionType catch(e) 通過catch可以捕獲異常對象e亚铁。如果沒有指定異常類型,直接使用catch(e)螟加,則表示可以捕獲任何異常類型徘溢。

只捕獲OutOfLlamasException異常:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}

對于可以拋出多種類型異常的代碼,你可以指定 多個捕獲語句:

void main() {
  try {
   // throw new FormatException("Hello"); 只觸發(fā)第一個on FormatException 
    
   // throw new TimeoutException("hello");只觸發(fā)第二個on Exception catch (e)
  
      throw "Hello EeveyOne"; //只觸發(fā)第三個catch (e)
      
  } on FormatException {
    //捕獲具體的異常類型FormatException捆探,沒有異常對象
    print("I'm a OutOfLlamasException");
  } on Exception catch (e) {
    //只要是Exception類型的異常都能捕獲然爆,并攜帶異常對象e
    print('Unknown exception: $e');  
  } catch (e) {
    // 任何throw拋出的類型都能捕獲
    print('Something really unknown: $e');
  }

}

如上面代碼所示,你可以使用on 或者 catch 來聲明捕獲語句黍图,也可以 同時使用曾雕。使用 on 來指定異常類型猾蒂,使用 catch 來 捕獲異常對象问畅。

函數(shù) catch() 可以帶有一個或者兩個參數(shù)鲸阻, 第一個參數(shù)為拋出的異常對象诬留, 第二個為堆棧信息 (一個 StackTrace 對象)。

void main() {
  try {
      throw "Hello EeveyOne";  
  }  on Exception catch (e) {
    print('Unknown exception: $e');
  } catch (e,s) { //e為異常對象搔弄,s為堆棧信息
    print('Exception details:\n $e');
    print('Stack trace:\n $s');
  }
}
img

使用 rethrow 關(guān)鍵字可以 把捕獲的異常給 重新拋出趾牧,當(dāng)然也可以使用 throw拋出異常,不過使用rethrow更方便:

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // Runtime error
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
    // throw  e;等價于上面
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

輸出:

misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.

2.3肯污、Finally

要確保某些代碼執(zhí)行,不管有沒有出現(xiàn)異常都需要執(zhí)行吨枉,可以使用 一個 finally 語句來實現(xiàn)蹦渣。<u>如果沒有 catch 語句來捕獲異常, 則在執(zhí)行完 finally 語句后貌亭, 異常被拋出了</u>:

void main() {
  try {
      throw "Hello EeveyOne";
  } finally{
      //finally執(zhí)行了柬唯,但是異常并沒有被捕獲,執(zhí)行后立馬拋出異常
    print("My Name Finally");
  }
    
   print("End");//沒有而被執(zhí)行
}

打印如下:

img

finally子句在所有匹配到的catch子句之后運行:

void main() {
  try {
      throw "Hello EeveyOne";
  } catch (e){
    print("Exception is $e");
  } finally{
    print("My Name Finally");
  }
  print("End");
}

輸出結(jié)果為:

Exception is Hello EeveyOne
My Name Finally
End

想了解更多請閱讀[Exceptions]章節(jié)圃庭。

歡迎關(guān)注我的公眾號【不喝咖啡的程序員】锄奢,最新的文章會在上面發(fā)布:


image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市剧腻,隨后出現(xiàn)的幾起案子拘央,更是在濱河造成了極大的恐慌,老刑警劉巖书在,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灰伟,死亡現(xiàn)場離奇詭異,居然都是意外死亡儒旬,警方通過查閱死者的電腦和手機栏账,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栈源,“玉大人挡爵,你說我怎么就攤上這事∩蹩眩” “怎么了茶鹃?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長制轰。 經(jīng)常有香客問我前计,道長,這世上最難降的妖魔是什么垃杖? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任男杈,我火速辦了婚禮,結(jié)果婚禮上调俘,老公的妹妹穿的比我還像新娘伶棒。我一直安慰自己旺垒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布肤无。 她就那樣靜靜地躺著先蒋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宛渐。 梳的紋絲不亂的頭發(fā)上竞漾,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音窥翩,去河邊找鬼业岁。 笑死,一個胖子當(dāng)著我的面吹牛寇蚊,可吹牛的內(nèi)容都是我干的笔时。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仗岸,長吁一口氣:“原來是場噩夢啊……” “哼允耿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扒怖,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤较锡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姚垃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體念链,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年积糯,在試婚紗的時候發(fā)現(xiàn)自己被綠了掂墓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡看成,死狀恐怖君编,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情川慌,我是刑警寧澤吃嘿,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站梦重,受9級特大地震影響兑燥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜琴拧,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一降瞳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦挣饥、人聲如沸除师。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汛聚。三九已至,卻和暖如春短荐,著一層夾襖步出監(jiān)牢的瞬間倚舀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工忍宋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瞄桨,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓讶踪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泊交。 傳聞我的和親對象是個殘疾皇子乳讥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔,今天18年5月份再次想寫文章廓俭,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,752評論 2 9
  • 上一篇: 一研乒、Flutter環(huán)境搭建下一篇: 三汹忠、Flutter基礎(chǔ)—ListView入門 我之前只有OC開發(fā)經(jīng)...
    OOOlive閱讀 4,150評論 4 13
  • Dart重要概念:1,在變量中可以放置的所有東西都是對象雹熬,而每個對象都是類的實例宽菜。無論數(shù)字、函數(shù)竿报、和null都是對...
    哥哥是歐巴Vitory閱讀 798評論 0 1
  • 此文章是v1.0+時編寫铅乡,年代久遠,小心有毒烈菌,謹(jǐn)慎食用U笮摇!芽世! 一些重要概念 所有的東西都是對象挚赊,所有的對象都是類的...
    soojade閱讀 10,051評論 2 27
  • 1、前言: 最近在學(xué)習(xí)Flutter济瓢,作為Flutter的編程語言荠割,Dart語言語法的學(xué)習(xí)自然就編程重中之重。本文...
    Yagami3zZ閱讀 1,497評論 0 0