Dart的語法詳解系列篇(1)---- 老司機用一篇博客帶你快速熟悉Dart語法

聲明:本文是作者AWeiLoveAndroid原創,版權歸作者AWeiLoveAndroid所有,侵權必究。如若轉發,請註明作者和來源地址!未經授權,嚴禁私自轉載!

________________________________________________________________________________________________

【前言】Dart語言是使用flutter框架開發時候必備的語言,flutter是一個跨平臺的框架,一套代碼就可以完美實現安卓和ios兩個平臺,適配也很不錯,Dart語言很友好,和java很類似,學習成本也是很低的。所以這也是我推薦學習Dart語言的一個原因。

從本篇文章開始講解Dart語言的基本使用,我將會連續推出好幾篇文章詳解,希望幫助大家快速掌握Dart語言。


本文目錄:

一、註釋
二、關鍵字(56個)
三、變量和常量
四、特殊數據類型
五、運算符
六、控制流程語句
七、異常


本文代碼同步發佈在Github:
https://github.com/AweiLoveAndroid/Flutter-learning/tree/master/projects/dart_demo

怎麼運行代碼?

如果你使用IDEA或者Android Studio:

打開IDEA或者Android Studio,新建一個Flutter項目,然後在test目錄運行我的代碼;或者裏面去寫你自己的dart文件,然後右鍵run就可以運行了。(注意:需要連接手機或者模擬器)。

如果你使用vscode:

打開vscode,執行菜單欄運行,就可以了(確保只有一個dart文件,免得運行的文件不是你想要的,就很尷尬了,vscode也可以設置默認運行的文件是哪個,但是新手不建議去設置,很麻煩。因爲你們想最快的運行效果,所有建議只有一個dart文件是最好的)。


一、註釋

Dart的註釋分爲3種:單行註釋、多行註釋、文檔註釋。

1、單行註釋以//開頭。Dart編譯器會忽略//和行尾之間的所有內容。

例如:// todo:待完成

2、多行註釋以/*開頭,以*/結尾。介於/**/兩者之間的內容會被編譯器忽略(除非該註釋是一個文檔註釋)。多行註釋可以嵌套。

例如:/* todo:待完成 */

3、文檔註釋以///或者/**開頭。可以通過dartdoc命令導出文檔。

文檔註釋的使用,例如:/// todo:待完成

文檔的導出如圖所示:

 

 
6098829-f958d7a9c526a705.png
文檔的導出

 

導出的結果在我的工程根路徑的/doc/api/文件夾裏面,如圖所示:

 
6098829-808c461eb43c34ad.png
導出的結果

然後瀏覽器打開index.html就可以看到文檔了。如圖所示:

 
6098829-f92d372334dd31ce.png
本地的文檔

二、關鍵字(60個)

5個上下文關鍵字(僅在特定位置具有含義。它們在任何地方都是有效的標識符)

關鍵字 - - -
async hide on show
sync - - -

其中內置標誌符有:(20個)

關鍵字 - - -
abstract as covariant defered
dynamic export external factory
Function get implements import
interface library mixin operator
part set static typedef

Dart新增的,有限的保留字,支持異步功能的關鍵字有:(2個)

關鍵字 - - -
await yield    

33個保留字(不能使用保留字作爲標識符)

關鍵字 - - -
assert break case catch
class const continue default
do else enum extends
false final finally for
if in is new
null rethrow return super
switch this throw true
try var void while
with - - -

跟java相比,Dart特有的關鍵字有:(27個)

關鍵字 - - -
as async await covariant
deferred dynamic export external
factory Function get hide
in is library mixin
on operator part rethrow
set show sync typedef
var with yield  

三、變量和常量

(一)變量的聲明,可以使用 var、Object 或 dynamic 關鍵字。

創建變量並初始化變量實例:

var name = '張三' ;

變量存儲引用。

    1. 使用Object或dynamic關鍵字
dynamic name = '張三';

調用的變量name包含對String值爲“張三” 的對象的引用。
name推斷變量的類型是String,但可以通過指定它來更改該類型。
如果對象不限於單一類型(沒有明確的類型),請使用Object或dynamic關鍵字

Object name = '張三';
dynamic name = '李四';
    1. 顯式聲明將被推斷的類型

比如String,int等。

//可以使用String顯示聲明字符串類型
String name = '張三' ; //代替var name = '張三';

這個類型有很多,具體在下文有介紹。

(二)默認值

未初始化的變量的初始值爲null(包括數字),因此數字、字符串都可以調用各種方法。

//測試 數字類型的初始值是什麼?
int intDefaultValue;
// assert 是語言內置的斷言函數,僅在檢查模式下有效
// 在開發過程中, 除非條件爲真,否則會引發異常。(斷言失敗則程序立刻終止)
assert(intDefaultValue == null);
print(intDefaultValue);//打印結果爲null,證明數字類型初始化值是null

(三)Final 和 Const的用法

如果您從未打算更改一個變量,那麼使用 final 或 const,不是var,也不是一個類型。
一個 final 變量只能被設置一次;const 變量是一個編譯時常量。(Const變量是隱式的final。)
final的頂級或類變量在第一次使用時被初始化。

  • 1、被final或者const修飾的變量,變量類型可以省略。
//可以省略String這個類型聲明
final name1 = "張三";
//final String name1  = "張三";
    
const name2 = "李四";
//const String name2 = "李四";
  • 2、被 final 或 const 修飾的變量無法再去修改其值。
final name1 = "張三";
// 這樣寫,編譯器提示:a final variable, can only be set once
// 一個final變量,只能被設置一次。
//name1 = "zhangsan";
    
const name2 = "李四";

// 這樣寫,編譯器提示:Constant variables can't be assigned a value
// const常量不能賦值
// name2 = "lisi";
  • 3、注意:flnal 或者 const 不能和 var 同時使用
//這樣寫都會報錯
//final var name1 = "張三";
//const var name2 = "李四";
  • 4、常量如果是類級別的,請使用 static const
static const speed = 100;
  • 5、常量的運算
const speed = 100; //速度(km/h)
const double distance = 2.5 * speed; // 距離 = 速度 * 時間

final speed2 = 100; //速度(km/h)
final double distance2 = 2.5 * speed2; // 距離 = 速度 * 時間
  • 6、const關鍵字不只是聲明常數變量。您也可以使用它來創建常量值,以及聲明創建常量值的構造函數。 任何變量都可以有一個常量值。
// 注意: [] 創建的是一個空的list集合
// const []創建一個空的、不可變的列表(EIL)。
var varList = const []; // varList 當前是一個EIL
final finalList = const []; // finalList一直是EIL
const constList = const []; // constList 是一個編譯時常量的EIL

// 可以更改非final,非const變量的值
// 即使它曾經具有const值
varList = ["haha"];

// 不能更改final變量或const變量的值
// 這樣寫,編譯器提示:a final variable, can only be set once
// finalList = ["haha"];
// 這樣寫,編譯器提示:Constant variables can't be assigned a value  
// constList = ["haha"];
  • 7、只要任何插值表達式是一個計算結果爲null或數字,字符串或布爾值的編譯時常量,那麼文字字符串就是編譯時常量。(關於$表達式和不同的數據類型後面會講解。)
// 這些是常量字符串
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 這些不是常量字符串

var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';

//這樣用就會報錯:Const variables must be initialized with a constant value
// const常量必須用conat類型的值初始化。
// const invalidConstString = '$aNum $aBool $aString $aConstList';

四、特殊數據類型

Dart支持以下特殊類型:

numbers 數字
strings 字符串
booleans 布爾
lists list集合(也稱爲數組)
maps map集合
runes 字符(用於在字符串中表示Unicode字符)

(一)num數字類型

num是數字類型的父類,有兩個子類 intdouble
num類型包括基本的運算符,如+,-,/和*,位運算符,如>>,在int類中定義。如果num和它的子類沒有你要找的東西,math庫可能會找到。比如你會發現abs(),ceil()和floor()等方法。

(1)int類型

int表示整數,int默認是64位二進制補碼整數,int的取值不大於64位,具體取決於平臺。編譯爲JavaScript時,整數僅限於valus,可以用雙精度浮點值精確表示。可用的整數值包括-253和253之間的所有整數,以及一些幅度較大的整數。這包括一些大於2^63的整數。 因此,在編譯爲JavaScript的Dart VM和Dart代碼之間,int類中的運算符和方法的行爲有時會有所不同。例如,當編譯爲JavaScript時,按位運算符將其操作數截斷爲32位整數。
示例如下:

int intNum1 = 10 ;
print(intNum1);//結果是10
int intNum2 = 0xDEADBEEF ;
print(intNum2);//結果是3735928559

判斷一個int值需要多少bit(位),可以使用bitLength,例如:

// bitLength 返回存儲此int整數所需的最小位數
int a1 = 1; // 佔了1個bit     相當於二進制數字 00000000 00000001
int a2 = 12; // 佔了4個bit    相當於二進制數字 00000000 00001100
int a3 = 123; // 佔了7個bit   相當於二進制數字 00000000 01111011
int a4 = 1234; // 佔了11個bit 相當於二進制數字 00000100 11010010
print('${a1.bitLength}'); //  1
print('${a2.bitLength}');  // 4
print('${a3.bitLength}'); // 7
print('${a4.bitLength}'); // 11

(2)double類型

Dart的double是IEEE 754標準中規定的64位浮點數。double的最大值是:1.7976931348623157e+308,double類裏面有一個常量maxFinite,我們通過語句print(double. maxFinite)可以得到double的最大值
如果一個數字包含一個小數,那麼它就是一個double類型。示例如下:

double doubleNum1 = 1.1;
print(doubleNum1); //結果是1.1
double doubleNum2 = 1.42e5;
print(doubleNum2); //結果是142000.0

(3)Dart2.1裏面新增特性,當double的值爲int值時,int自動轉成double。

例如:double test = 12;//打印結果是12.0

(4)Dart2.1,int也有api轉成double。

例如:

int test = 10;
print(test.toDouble()); // 結果是: 10.0

(5)Dart2.1,double也有api轉成int,會把小數點後面的全部去掉。

例如:

double test2 = 15.1;
double test3 = 15.1234;
print(test2.toInt());// 結果是15
print(test3.toInt());// 結果是15

(二)String字符串類型

Dart裏面的String是一系列 UTF-16代碼單元。

(1)您可以使用單引號或雙引號來創建一個字符串。

String str1 = '單引號基本使用demo.';
String str2 = "雙引號基本使用demo.";

(2)單引號或者雙引號裏面嵌套使用引號。

單引號裏面嵌套單引號,或者//雙引號裏面嵌套雙引號,必須在前面加反斜槓。

// 單引號裏面有單引號,必須在前面加反斜槓
String str3 = '單引號裏面有單引號it\'s,必須在前面加反斜槓.';
//雙引號裏面嵌套單引號(正常使用)
String str4 = "雙引號裏面有單引號it's.";
//單引號裏面嵌套雙引號(正常使用)
String str5 = '單引號裏面有雙引號,"hello world"';
//雙引號裏面嵌套雙引號,必須在前面加反斜槓
String str6 = "雙引號裏面有雙引號,\"hello world\"";

print(str3);// 雙引號裏面有單引號it's,必須在前面加反斜槓
print(str4);// 雙引號裏面有單引號it's.
print(str5);// 單引號裏面有雙引號,hello world"
print(str6);//雙引號裏面有雙引號,"hello world"

(3)多個字符串相鄰中間的空格問題:

除了單引號嵌套單引號或者雙引號嵌套雙引號不允許出現空串之外,其餘的幾種情況都是可以運行的。
示例如下:

這個會報錯
//String blankStr1 = 'hello''''world';
//這兩個運行正常
String blankStr2 = 'hello'' ''world'; //結果: hello world
String blankStr3 = 'hello''_''world'; //結果: hello_world


// 這個會報錯
//String blankStr4 = "hello""""world";
這兩個運行正常
String blankStr5 = "hello"" ""world"; //結果: hello world
String blankStr6 = "hello""_""world"; //結果: hello_world

單引號裏面有雙引號,混合使用運行正常

String blankStr7 = 'hello""""world'; //結果: hello""""world
String blankStr8 = 'hello"" ""world'; //結果: hello"" ""world
String blankStr9 = 'hello""_""world'; //結果: hello""_""world

雙引號裏面有單引號,混合使用運行正常

String blankStr10 = "hello''''world"; //結果: hello''''world
String blankStr11 = "hello'' ''world"; //結果: hello'' ''world
String blankStr12 = "hello''_''world"; //結果: hello''_''world

(4)您可以使用相鄰字符串文字或+ 運算符連接字符串:

  1. 直接把相鄰字符串寫在一起,就可以連接字符串了。
  String connectionStr1 =  '字符串連接''甚至可以在''換行的時候進行。';
  print(connectionStr1);// 字符串連接甚至可以在換行的時候進行。
  1. 用+把相鄰字符串連接起來。
  String connectionStr2 =  '字符串連接'+ '甚至可以在'+ '換行的時候進行。';
  print(connectionStr2);// 字符串連接甚至可以在換行的時候進行。
  1. 使用單引號或雙引號的三引號:
String connectionStr3 = ''' 
  這是用單引號創建的
  多行字符串。
  ''' ;
print(connectionStr3);
String connectionStr4 = """這是用雙引號創建的
  多行字符串。""";
print(connectionStr4);

print(connectionStr3)輸出結果如下:

  這是用單引號創建的
  多行字符串。

print(connectionStr4)的輸出結果如下:

這是用雙引號創建的
  多行字符串。

(5)關於轉義符號的使用

聲明raw字符串(前綴爲r),在字符串前加字符r,或者在\前面再加一個\
可以避免“\”的轉義作用,在正則表達式裏特別有用。

舉例如下:

print(r"換行符:\n"); //這個結果是 換行符:\n
print("換行符:\\n"); //這個結果是 換行符:\n
print("換行符:\n");  //這個結果是 換行符:

(6)使用$可以獲得字符串中的內容,使用${表達式}也可以將表達式的值放入字符串中。使用${表達式}時可以使用字符串拼接,也可以使用String類或者Object裏面的某些方法獲得相關字符串屬性。

1、使用$+字符串

var height = 48.0;
print('當前標題的高度是$height'); //當前標題的高度是48.0

2、使用$+字符串,以及字符串拼接

String name = "張三";
print("$name"+"是我們的部門經理"); // 張三是我們的部門經理

3、這裏使用了字符串的拼接,以及使用了String類裏面的toUpperCase()函數,把字母全部變成大寫。

String replaceStr = 'Android Studio';
assert('你知道' +
'${replaceStr.toUpperCase()}'
+ '最新版本是多少嗎?' ==
'你知道ANDROID STUDIO最新版本是多少嗎?'); 

注:==操作符測試兩個對象是否相等。assert是斷言,如果條件爲true,繼續進行,否則拋出異常,中端操作。

(三)bool布爾類型

Dart表示布爾值的類型叫做bool,它有兩個值,分別是:truefalse,它們都是編譯時常量。
Dart使用的是顯式的檢查值,檢查值的類型,如下所示:

  // 檢查是否爲空字符串
  var emptyStr = '';
  assert(emptyStr.isEmpty);

  // 檢查是否小於等於0
  var numberStr = 0;
  assert(numberStr <= 0);  

  // 檢查是否爲null
  var nullStr;
  assert(nullStr == null);

  // 檢查是否爲NaN
  var value = 0 / 0;
  assert(value.isNaN);

assert 是Dart語言裏的的斷言函數,僅在Debug模式下有效
在開發過程中, 除非條件爲真,否則會引發異常。(斷言失敗則程序立刻終止)。

(四)list集合,也成爲數組

在Dart中,數組是List對象,因此大多數人只是將它們稱爲List。
以下是一個簡單的Dart的List:

創建一個int類型的list

List list = [10, 7, 23];
print(list);// 輸出結果  [10, 7, 23]

要創建一個編譯時常量const的list,示例如下:

List constantList = const[10,3,15];
print(constantList);// 輸出結果  [10, 3, 15]

注意事項:

1.可以直接打印list包括list的元素,list也是一個對象。但是java必須遍歷才能打印list,java若直接打印list,結果是地址值。
2.和java一樣list裏面的元素必須保持類型一致,不一致就會報錯。
3.和java一樣list的角標從0開始。

Dart的list集合給我們提供了很多api,示例如下,api太多就不逐個展示了:

操作 代碼 含義 輸出結果
新增 list.add(1);print(list); 把數字1添加到list中,默認是添加到末尾 [10, 7, 23, 1]
移除 list.remove(1);print(list); 移除數字1 [10, 7, 23]
插入 list.insert(0, 5);print(list); 在索引爲0的地方插入數字5 [5, 10, 7, 23]
查找某個索引的值 int value = list.indexOf(10);print(value); 查找10在list中的索引 1
判斷元素是否包含 bool result = list.contains(5);print(result); 查找list中是否包含數字5 true

(五)map集合

Dart中的map是將鍵和值相關聯的對象。鍵和值都可以是任何類型的對象。每個鍵只出現一次,但您可以多次使用相同的值。

(1)創建方式:

    1. 直接聲明,用{}表示,裏面寫key和value,每組鍵值對中間用逗號隔開。
Map companys = {'first': '阿里巴巴', 'second': '騰訊', 'fifth': '百度'};
print(companys);//打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度}
    1. 先聲明,再去賦值。
  Map companys1 = new Map();
  companys1['first'] = '阿里巴巴';
  companys1['second'] = '騰訊';
  companys1['fifth'] = '百度';
  print(companys1);
  //打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度}
    1. 要創建一個編譯時常量const的map,請在map文字之前添加const:
final fruitConstantMap = const {2: 'apple',10: 'orange',18: 'banana'};
// 打印結果{second: 騰訊, fifth: 百度, 5: 華爲}

(2)添加元素。格式: 變量名[key] = value,其中key可以是不同類型。

//添加一個新的元素,key爲“5”,value爲“華爲”
  companys[5] = '華爲';
  print(companys);//打印結果 {first: 阿里巴巴, second: 騰訊, fifth: 百度, 5: 華爲}

(3)修改元素。格式:變量名[key] = value

例如:把key爲first的元素對應的value改成 alibaba

  companys['first'] = 'alibaba';
  print(companys);//打印結果 {first: alibaba, second: 騰訊, fifth: 百度, 5: 華爲}

(4)查詢元素

  bool mapKey = companys.containsKey('second');
  bool mapValue = companys.containsValue('百度');
  print(mapKey); //結果爲:true
  print(mapValue); //結果爲:true

(5)刪除元素.可以使用map的remove或者clear方法。

  companys.remove('first');// 移除key爲“first”的元素。
  print(companys);// 打印結果{second: 騰訊, fifth: 百度, 5: 華爲}

  companys.clear();// 清空map集合的數據。
  print(companys);// 打印結果{}

(6)關於map集合的小結:

1.創建map有兩種方式。
2.map的key類型不一致也不會報錯。
3.添加元素的時候,會按照你添加元素的順序逐個加入到map裏面,哪怕你的key不連續。
比如key分別是 1,2,4,看起來有間隔,事實上添加到map的時候{1:value,2:value,4:value} 這種形式。
4.添加的元素的key如果是map裏面某個key的英文,照樣可以添加到map裏面,
比如可以爲3和key爲three可以同時存在。
5.map裏面的key不能相同,但是value可以相同,value可以爲空字符串或者爲null。

(六)runes 字符(用於在字符串中表示Unicode字符)

Unicode爲世界上所有的書寫系統中使用的每個字母,數字和符號定義了唯一的數值。
Dart字符串是UTF-16代碼單元的序列,所以在字符串中表達32位Unicode值需要特殊的語法。
Unicode代碼點的常用方法是\uXXXX,其中XXXX是一個4位十六進制值。

例如,心形字符()是\u2665。要指定多於或少於4個十六進制數字,請將該值放在大括號中。 例如,笑的表情符號是\u{1f600}

String類有幾個屬性可以用來提取符文信息。 codeUnitAt和codeUnit屬性返回16位代碼單元。
以下示例說明了符文,16位代碼單元和32位代碼點之間的關係。

var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());

//使用String. fromCharCodes顯示字符圖形 
Runes input = new Runes(
        '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
print(new String.fromCharCodes(input));

五、運算符

運算符在每一種語言中都很常見,Dart的運算符如下表所示:

 
6098829-58d0dd182cb1ac4b.png
 

我這裏不詳細去講解每個運算符的用法,我這裏主要講一下Dart裏面比較有代表性的以及有特點的一些運算符相關用法。

(一)?..一樣,但最左邊的操作數可以爲空。

比如:Test?.funs從表達式Test中選擇屬性funs,除非Test爲空(當Test爲空時,Test?.funs的值爲空)。

class Test {
  static int funs = 5;

  Test() {
    print('構造函數 Test');
  }
  static fun() {
    print('Test fun函數');
  }
}
void main(){
  print(Test?.funs); // 打印5
}

(二)..級聯符號..

級聯符號..允許您在同一個對象上進行一系列操作。 除了函數調用之外,還可以訪問同一對象上的字段。其實相當於java的鏈式調用。
例如:

String s = (new StringBuffer()
        ..write('test1 ')
        ..write('test2 ')
        ..write('test3 ')
        ..write('test4 ')
        ..write('test5'))
      .toString();
print(s); // test1 test2 test3 test4 test5

(三)?? 三目運算符的一種形式。

expr1 ?? expr2 表示如果expr1非空,則返回其值; 否則返回expr2的值。

//普通三元運算符
int a = 10;
var values = a > 5 ? a : 0;
//??操作符
print('a ??=3 : ${a ??= 3}');  // 3

(四)~/ 除法,返回一個整數結果,其實就是取商。

小學都學過:被除數 ÷ 除數 = 商 ... 餘數,在Dart裏面A ~/ B = C,這個C就是商,這個語句相當於Java裏面的A / B = C。Dart與java不同的是,Dart裏面如果使用A / B = D語句,這個結果計算出來的是真實的結果。示例如下:

  var result1 = 15/7;
  print(result1); // 結果是:2.142857...
  var result2 = 15~/7;
  print(result2); // 結果是:2

順便提一下取模操作,在Dart裏面A % B = E,這個E就是餘數,%符號表示取模,例如:

 var result3 = 15%7;
  print(result3); // 結果是:1

(五)as、is與is!

as 判斷屬於某種類型
is 如果對象具有指定的類型,則爲true
is! 如果對象具有指定的類型,則爲false

例如:

class Test {
  static int funs = 5;

  Test() {
    print('構造函數 Test');
  }
  static fun() {
    print('Test fun函數');
  }
}

class Test2 extends Test {
  Test2() {
    print('構造函數 Test2');
  }
  void fun() {
    print('Test2 fun函數');
  }
}

void main(){
  print(test2 is Test);  // true
  print(test is! Test2);  // true

  (test2 as Test2).fun();  // Test2 fun函數
  // 相當於
  // if (test2 is Test) {
  //   test2.fun();
  // }

六、控制流程語句

控制流程語句和Java語言差不多,有這些語句:

(一)if else

if(條件語句){
    內容體
}else{
內容體
}

(二)for循環

1.簡單for循環

for(初始值;判斷條件;循環後的語句){
    內容體
}

例如:

for(int i=0;i<10;i++){
    print(i);
}

也可以通過for循環內部的閉包獲取索引的值。

var array = [];
for(var i=0;i<10;i++){
    array.add(()=> print(i));
}

2.使用foreach循環,一般List和Set都可以使用foreach遍歷元素。

如果要迭代的對象是Iterable,或者你不想知道當前的迭代次數,可以使用foreach()方法。

var numbers = [1,2,3,4,5,6,7,8,9];
numbers.foreach((number)=> print(number));

3.使用for in循環,一般List和Set使用for-in遍歷元素。

var list = [1,2,3];
for(var data in list){
    print(data);
}

4.Dart的for循環裏面可以使用標記:(比較有特色的地方)

Dart的標記:標記是後面跟着冒號的標識符。帶標記的陳述是以標記 L爲前綴的陳述。帶標籤的case子句是標籤L前綴的switch語句中的case子句。標籤的唯一作用是爲“break”和“continue”聲明提供對象。
大多數此功能與其他語言類似,因此以下大部分內容可能對讀者來說都很熟悉。Dart的switch聲明中處理continue是比較獨特的,所以這一部分需要一點時間去閱讀和熟悉。

  • 1.循環(Loops)

標籤最常用作breakcontinue內部循環。假設你有嵌套的循環,並要跳轉到breakcontinue到外部循環。如果沒有標記,這不可能(輕鬆)實現。

以下示例使用continue 標記名稱從內部循環直接跳轉到外部循環的下一輪循環:

// 返回具有最小總和的內部列表(正整數)。
/// Returns the inner list (of positive integers) with the smallest sum.
List<int> smallestSumList(List<List<int>> lists) {
  var smallestSum = 0xFFFFFFFF; //已知list的總和較小。
  var smallestList = null;
  outer: // 這就是標記
  for (var innerList in lists) {
    var sum = 0;
    for (var element in innerList) {
      assert(element >= 0);
      sum += element;
      // 無需繼續迭代內部列表。它的總和已經太高了。
      if (sum > smallestSum) continue outer; // continue 跳出到標記處(outer)
    }
    smallestSum = sum;
    smallestList = innerList;
  }
  return smallestList;
}

此函數在所有list中運行,但只要總和過高,就會停止累加變量。

同理,可以使用break跳出到外部循環:

// 計算第一個非空list
List<int> firstListWithNullValueList(List<List<int>> lists) {
  var firstListWithNullValues = null;
  outer:
  for (var innerList in lists) {
    for (var element in innerList) {
      if (element == null) {
        firstListWithNullValues = innerList;
        break outer;  // break 返回到標記處
      }
    }
  }
  // 現在繼續正常的工作流程
  if (firstListWithNullValues != null) {
    // ...
  }
  return firstListWithNullValues;
}
  • 2.跳出代碼塊

標記也可以用於跳出代碼塊。假設我們想要統一處理錯誤條件,但有多個條件(可能是深度嵌套)來揭示(reveal)錯誤。標籤可以幫助構建此代碼。

void doSomethingWithA(A a) {
  errorChecks: {
    if (a.hasEntries) {
      for (var entry in a.entries) {
        if (entry is Bad) break errorChecks;   // 跳出代碼塊
      }
    }
    if (a.hasB) {
      var b = new A.b();
      if (b.inSomeBadState()) break errorChecks;  // 跳出代碼塊
    }
    // 一些看起來都正常
    use(a);
    return;
  }
  // 錯誤的情況,執行這裏的代碼:
  print("something bad happened");
}

class A{
  bool hasEntries = false;
  bool hasB = true;
  List<Bad> entries = [new Bad2(),new Bad2()];
  A.b(){

  }

  bool inSomeBadState(){
    return false;
  }
  
}

void use(A a){}

abstract class Bad{}
class Bad1 extends Bad{}
class Bad2 extends Bad{}

對代碼塊的使用break指令,使得Dart繼續執行塊之後的語句。從某個角度來看,它是一個結構化的goto,它只允許跳轉到當前指令之後的嵌套較少的位置。

雖然聲明標籤在代碼塊中最有用,但它們可以用在在每個語句中。
例如,foo: break foo;是一個有效的聲明。

請注意:continue上面的循環可以通過將循環體包裝到帶標記的代碼塊中並使用break來實現。
也就是說,以下兩個循環是等效的:

//以下兩種描述是等價的:

// 使用 continue
for (int i = 0; i < 10; i++) {
  if (i.isEven) continue;
  print(i);
}

// 使用 break.
for (int i = 0; i < 10; i++) {
  labels: {
    // isEven 當且僅當該整數爲偶數時才返回true
    if (i.isEven) break labels;
    print(i);
  }
}
  • 3.Switch中的標記(label)

標記也可以用於switch內部。
Switch中的標記允許continue 使用其它的case 子句。在最簡單的形式中,這可以作爲一種方式來實現下一個子句:

void switchExample(int foo) {
  switch (foo) {
    case 0:
      print("foo is 0");
      break;
    case 1:
      print("foo is 1");
      continue shared; // Continue使用在被標記爲shared的子句中
    shared:
    case 2:
      print("foo is either 1 or 2");
      break;
  }
}

有趣的是, Dart沒有要求continue的目標子句是當前case子句之後的子句。
帶標記的任何case子句都是有效的目標。這意味着,Dart的switch語句實際上是狀態機(state machines)。

以下示例演示了這種濫用,其中整個switch實際上只是用作狀態機(state machines)。

void main() {
  runDog();
}

void runDog() {
  int age = 0;
  int hungry = 0;
  int tired = 0;

  bool seesSquirrel() => new Random().nextDouble() < 0.1;
  bool seesMailman() => new Random().nextDouble() < 0.1;

  switch (1) {
    start:
    case 0:
      print("dog 方法已經開始");
      print('case 0 ==> age: $age');
      print('case 0 ==> hungry: $hungry');
      print('case 0 ==> tired: $tired');
      continue doDogThings;

    sleep:
    case 1:
      print("sleeping");
      tired = 0;
      age++;
      if (age > 20) break;
      print('case 1 ==> age: $age');
      print('case 1 ==> hungry: $hungry');
      print('case 1 ==> tired: $tired');
      continue doDogThings;

    doDogThings:
    case 2:  
      if (hungry > 2) continue eat;
      if (tired > 3) continue sleep;
      if (seesSquirrel()) continue chase;
      if (seesMailman()) continue bark;
      print('case 2 ==> age: $age');
      print('case 2 ==> hungry: $hungry');
      print('case 2 ==> tired: $tired');
      continue play;

    chase:
    case 3:  
      print("chasing");
      hungry++;
      tired++;
      print('case 3 ==> age: $age');
      print('case 3 ==> hungry: $hungry');
      print('case 3 ==> tired: $tired');
      continue doDogThings;

    eat:
    case 4:  
      print("eating");
      hungry = 0;
      print('case 4 ==> age: $age');
      print('case 4 ==> hungry: $hungry');
      print('case 4 ==> tired: $tired');
      continue doDogThings;

    bark:
    case 5: 
      print("barking");
      tired++;
      print('case 5 ==> age: $age');
      print('case 5 ==> hungry: $hungry');
      print('case 5 ==> tired: $tired');
      continue doDogThings;

    play:
    case 6: 
      print("playing");
      tired++;
      hungry++;
      print('case 6 ==> age: $age');
      print('case 6 ==> hungry: $hungry');
      print('case 6 ==> tired: $tired');
      continue doDogThings;
  }
}

這個函數從一個switch子句跳到另一個子句,模擬了狗的生命。
在Dart中,標籤只允許在case子句中使用,因此我必須添加一些case永遠不會到達的行。

這個功能很酷,但很少使用。由於我們的編譯器增加了複雜性,我們經常討論它的刪除。到目前爲止,它已經在我們的審查中倖存下來,但我們最終可能會簡化我們的規範並讓用戶自己添加一個while(true)循環(帶有標記)。這個dog的示例可以重寫如下:

var state = 0;
loop:
while (true)
  switch (state) {
    case 0:
      print("dog has started");
      state = 2; continue;

    case 1:  // sleep.
      print("sleeping");
      tired = 0;
      age++;
      // The inevitable... :(
      if (age > 20) break loop;  // 跳出循環
      // Wake up and do dog things.
      state = 2; continue;
    
    case 2:  // doDogThings.
      if (hungry > 2) { state = 4; continue; }
      if (tired > 3) { state = 1; continue; }
      if (seesSquirrel()) { state = 3; continue; }
      ...

如果狀態值被命名爲常量,那麼它將與原始版本一樣具有可讀性,但不需要switch語句來支持狀態機。

(三)while 和do while

while(判斷條件){
    內容體
}
do{
內容體
} while(判斷條件);
while(a>5){
    print(a);
}
do{
print(a);
} while(a>5);

(四)break continue

break 停止循環

while(a>5){
  if(b>5){
  print(a);
    break;
  }
}

continue 跳到下一個循環

while(a>5){
  if(b<10){
  print(b);
    continue;
  }
}

如果使用Iterable(list或者set),則可以使用下面這種方式:

// 第一個是滿足條件就進入  第二個是foreach遍歷
arrays
  .when((c)=>c.counts >=5)
  .foreach((c)=>c.next());

(五)switch case

比較integer, string,編譯時常量 使用==。比較對象必須是同一個類的實例(不是其子類的實例),並且該類不能重寫==。枚舉類型在switch也可以運行。
每一條非空case字子句以break結束,也可以使用其他的方式結束:continue,throw或者return

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

(六)assert

如果布爾條件爲false,則使用assert語句來中斷正常執行。例如:

// 確保變量具有非空值 
assert(text != null);
// 確保值小於100
assert(number < 100);
// 確保這是一個 https 網址
assert(urlString.startsWith('https'));

要將消息附加到斷言,請添加一個字符串作爲第二個參數。

assert(urlString.startsWith('https'),'URL ($urlString) should start with "https".');

上例中assert的第一個參數可以是任何解析爲布爾值的表達式。如果表達式的值爲true,則斷言成功並繼續執行。如果爲false,則斷言失敗並拋出異常


七、異常

Dart代碼可以拋出並捕獲異常。Exception是指示發生意外事件的錯誤。如果未捕獲異常,則會暫停引發異常的isolate ,並且通常會終止isolate及其程序。

與Java相比,Dart的所有異常都是未經檢查的異常。方法不會聲明它們可能引發的異常,並且您不需要捕獲任何異常。

Dart提供了ExceptionError 類型,以及許多預定義的子類型。當然,您可以定義自己的Exception。但是,Dart程序可以拋出任何非null對象,作爲Exception(不僅僅是Exception和Error對象)。

(一)throw

以下是拋出或引發異常的示例:

throw FormatException('Expected at least 1 section');

你也可以拋出任意對象,例如:throw '格式不正確!';
通常在開發中會拋出Error或者Exception類型。

因爲拋出異常是一個表達式,所以可以在=>語句中以及允許表達式的任何其他地方拋出異常:

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

(二)try catch

捕獲或捕獲異常會阻止異常傳遞(除非您重新拋出異常)。捕獲異常使您有機會處理它:

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

要處理可能拋出多種類型異常的代碼,可以指定多個catch子句。與拋出對象的類型匹配的第一個catch子句處理異常。如果catch子句未指定類型,則該子句可以處理任何類型的拋出對象。
您可以使用on或catch兩者兼而有之。使用on時需要指定異常類型。使用catch時,你的異常處理程序需要異常對象。
示例:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

您可以指定一個或兩個參數catch()。第一個是拋出的異常,第二個是堆棧跟蹤(StackTrace對象)。
示例:

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

要部分處理異常,同時允許它傳遞,請使用rethrow關鍵字。
示例:

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 運行時異常
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 允許調用者查看exception.
  }
}

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

(三)finally

無論是否拋出異常,要確保某些代碼運行,請使用finally子句。如果沒有catch子句匹配該異常,則在finally子句運行後傳遞異常。
示例:

try {
  breedMoreLlamas();
} finally {
  // 即使拋出異常  也會執行這句代碼.
  cleanLlamaStalls();
}
該finally子句在任何匹配的catch子句之後運行:
try {
  breedMoreLlamas();
} catch (e) {
    // 首先會處理異常
  print('Error: $e'); 
} finally {
  // 然後執行這句語句
  cleanLlamaStalls(); 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章