Dart語言簡介

Dart 初體驗

我們在Android Studio中建立一個main.dart文件,右鍵Run main.dart

void sayHello(String name) {
  print('Hello world, I am $name');
}
main() {
  String myName = 'xiaoming';
  sayHello(myName);
}

可以看到控制臺會輸出

Hello world, I am xiaoming

和絕大多數編譯型語言一樣,Dart 要求以 main 函數作為執行的入口。

Dart 的變量與類型

在 Dart 中,我們可以用 var 或者具體的類型來聲明一個變量。當使用 var 定義變量時,表示類型是交由編譯器推斷決定的,這樣的話,類型推斷會耗費一些性能,可以用靜態類型去定義變量,更清楚地跟編譯器表達你的意圖,這樣編輯器和編譯器就能使用這些靜態類型,向你提供代碼補全或編譯警告的提示。

  • 在默認情況下,未初始化的變量值都是 null,不用擔心JavaScript中未定義的值是undefined的情況。
  • Dart 是類型安全的語言,并且所有類型都是對象類型,都繼承自頂層類型 Object,因此一切變量的值都是類的實例(即對象),甚至數字、布爾值、函數和 null 也都是繼承自 Object 的對象。
  • Dart 內置了一些基本類型,如 num、bool、String、List 和 Map,在不引入其他庫的情況下可以使用它們去聲明變量。

dynamic,var,object的區別

var a = 1;
object b = 1;
dynamic c = 1;

上面的例子中,看起來三者非常相似,但是背后的原理卻是非常不同。
var本身是一個語法,并不能說是一種類型,其它的兩者object和dynamic是類型。

var聲明的變量在賦值的那一刻,就已經決定了它是什么類型。
所以如果你這樣使用,就會有編譯錯誤:

var a = 1;
a = "Test";

object之所以能夠被賦值為任意類型的原因,是因為所有的類型都派生自object. 所以它可以賦值為任何類型:

object a = 1;
a = "Test";

dynamic不是在編譯時候確定實際類型的, 而是在運行時。
所以下面的代碼是能夠通過編譯的,但是會在運行時報錯:

dynamic a = "test";
a++;

dynamic類型具有所有可能的屬性和方法。Dart語言中函數方法都有dynamic類型作為函數的返回類型,函數的參數也都有dynamic類型。

final和const
如果從未打算更改一個變量,那么使用 final 或 const,不是var,也不是一個類型。 一個 final 變量只能被設置一次,兩者區別在于:const 變量是一個編譯時常量,final變量在第一次使用時被初始化。被final或者const修飾的變量,變量類型可以省略,如:

//可以省略String這個類型聲明
final str1 = "hello world";
//final String str1 = "hello world"; 
const str2 = "hello world";
//const String str2 = "hello world";

函數

Dart是一門真正的面向對象的語言,所以即使是函數也是對象,并且有一個類型Function。這意味著函數可以賦值給變量或作為參數傳遞給其他函數,這是函數式編程的典型特征。

函數聲明

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

Dart函數聲明如果沒有顯式聲明返回值類型時會默認當做dynamic處理。

typedef bool CALLBACK();

//不指定返回類型,此時默認為dynamic,不是bool
isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void test(CALLBACK cb){
   print(cb()); 
}
//報錯,isNoble不是bool類型
test(isNoble);

函數作為變量

var say = (str){
  print(str);
};
say("hi world");

函數作為參數傳遞

void execute(var callback) {
    callback();
}
execute(() => print("hello world"))

可選的位置參數

String say(String name, String msg, [String device]) {
  var result = '$name says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

下面是一個不帶可選參數調用這個函數的例子:

say('xiaoming', 'hello'); //結果是: xiaoming says hello

下面是用第三個參數調用這個函數的例子:

say('xiaoming', 'hello', 'android'); //結果是:xiaoming says hello with a android

可選的命名參數
定義函數時,使用{param1, param2, …},放在參數列表的最后面,用于指定命名參數。例如:

//設置[bold]和[hidden]標志
void enableFlags({bool bold, bool hidden}) {
    // ... 
}

調用函數時,可以使用指定命名參數。例如:paramName: value
enableFlags(bold: true, hidden: false);
Flutter 中會大量用到可選命名參數的方式,一定要記住它的用法。

注意,不能同時使用可選的位置參數和可選的命名參數

Dart 是面向對象的語言,每個對象都是一個類的實例,都繼承自頂層類型 Object。在 Dart 中,實例變量與實例方法、類變量與類方法的聲明與 Java 類似,值得一提的是,Dart 中并沒有 public、protected、private 這些關鍵字,我們只要在聲明變量與方法時,在前面加上“”即可作為 private 方法使用。如果不加“”,則默認為 public。不過,“_”的限制范圍并不是類訪問級別的,而是庫訪問級別。
接下來,我們以一個具體的案例看看Dart 是如何定義和使用類的。建立一個test.dart文件。在 Calc 類中,定義了兩個成員變量 x 和 y,通過構造函數語法糖進行初始化,成員函數 printInfo 的作用是打印它們的信息;而類變量 z,則在聲明時就已經賦好了默認值 0,類函數 sum 會打印出它的信息。

class Calc {
  int x, y;
  static num z = 0;
  Calc(this.x,this.y); // 語法糖,等同于在函數體內:this.x = x;this.y = y;
  void printInfo() => print('($x, $y)');
  static void sum(int a, int b) => print('result ${a + b }');
}

void main() {
  Calc c = new Calc(100,200); // new 關鍵字可以省略
  c.printInfo();  // 輸出 (100, 200);
  Calc.z = 10;
  Calc.sum(10, 20); // 輸出 result 40
}

有時候類的實例化需要根據參數提供多種初始化方式。除了可選命名參數和可選參數之外,Dart 還提供了命名構造函數的方式,使得類的實例化過程語義更清晰。
此外,與 C++ 類似,Dart 支持初始化列表。在構造函數的函數體真正執行之前,你還有機會給實例變量賦值,甚至重定向至另一個構造函數。
如下面實例所示,Calc 類中有兩個構造函數 Calc.reDirct 與 Calc,其中:Calc.reDirct將其成員變量的初始化重定向到了 Calc 中,而 Calc 則在初始化列表中為 z 賦上了默認值 0。

class Calc {
  num x, y, z;
  Calc(this.x, this.y) : z = 0; // 初始化變量 z
  Calc.reDirct(num x) : this(x, 0); // 重定向構造函數
  void printInfo() => print('($x,$y,$z)');
}

void main() {
  Calc c1 = Calc(100,200); // new 關鍵字可以省略
  c1.printInfo();  // 輸出 (100,200,0)
  Calc c2 = Calc.reDirct(300);
  c2.printInfo();  // 輸出 (300,0,0)
}

復用

在面向對象的編程語言中,將其他類的變量與方法納入本類中進行復用的方式一般有兩種:繼承父類和接口實現。當然,在 Dart 也不例外。
在 Dart 中,你可以對同一個父類進行繼承或接口實現:

  • 繼承父類意味著,子類由父類派生,會自動獲取父類的成員變量和方法實現,子類可以根據需要覆寫構造函數及父類方法;
  • 接口實現則意味著,子類獲取到的僅僅是接口的成員變量符號和方法符號,需要重新實現成員變量,以及方法的聲明和初始化,否則編譯器會報錯
class Parent {
  num x = 0, y = 0;
  void printInfo() => print('($x,$y)');
}

//Children1 繼承自 Parent
class Children1 extends Parent {
  num z = 0;
  @override
  void printInfo() => print('($x,$y,$z)'); // 覆寫了 printInfo 實現
}

//Children2 是對 Parent 的接口實現
class Children2 implements Parent {
  num x = 0, y = 0; // 成員變量需要重新聲明
  void printInfo() => print('($x,$y)'); // 成員函數需要重新聲明實現
}

void main() {
  var xxx = Children1();
  xxx
    ..x = 1
    ..y = 2
    ..z = 3; // 級聯運算符,等同于 xxx.x=1; xxx.y=2;xxx.z=3;
  xxx.printInfo(); // 輸出 (1,2,3)

  var yyy = Children2();
  yyy
    ..x = 1
    ..y = 2; // 級聯運算符,等同于 yyy.x=1; yyy.y=2;
  yyy.printInfo(); // 輸出 (1,2)
  print(yyy is Parent); //true
  print(yyy is Children2); //true
}

可以看出,子類 Children2采用接口實現的方式,僅僅是獲取到了父類Parent 的一個“空殼子”,只能從語義層面當成接口 Parent 來用,但并不能復用 Parent 的原有實現。那么,我們是否能夠找到方法去復用 Parent 的對應方法實現呢?也許你很快就想到了,可以讓 Children2 繼承 Point,來復用其對應的方法。但如果 Children2 還有其他的父類,我們又該如何處理呢?

除了繼承和接口實現之外,Dart 還提供了另一種機制來實現類的復用,即“混入”(Mixin)。混入鼓勵代碼重用,可以被視為具有實現方法的接口。這樣一來,不僅可以解決 Dart 缺少對多重繼承的支持問題,還能夠避免由于多重繼承可能導致的歧義(菱形問題)。

菱形問題,是支持多繼承的編程語言中一個相當棘手的問題。當 B 類和 C 類繼承自 A 類,而 D 類繼承自 B 類和 C 類時會產生歧義。如果 A 中有一個方法在 B 和 C 中已經覆寫,而 D 沒有覆寫它,那么 D 繼承的方法的版本是 B 類,還是 C 類的呢?

要使用混入,只需要 with 關鍵字即可

class Children2 with Parent {
}
 
var yyy = Children2();
print (yyy is Parent); //true
print(yyy is Children2); //true

運算符

Dart 和絕大部分編程語言的運算符一樣,你可以用熟悉的方式去執行程序代碼運算。不過,Dart 多了幾個額外的運算符,用于簡化處理變量實例缺失(即 null)的情況。

  • ?.運算符:p?.printInfo()表示 p 為 null 的時候跳過,避免拋出異常。
  • ??= 運算符:a ??= value 表示,如果 a 為 null,則給 a 賦值 value,否則跳過。
  • ??運算符:a ?? b表示如果 a 不為 null,返回 a 的值,否則返回 b。在 Java 中,我們需要通過三元表達式 (a != null)? a : b 來實現這種情況。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • 標簽(空格分隔): Dart Flutter Dart在靜態語法方面和Java非常相似,如類型定義、函數聲明、泛型...
    黃昭鴻閱讀 422評論 0 0
  • Dart的設計目標應該是既對標Java,也對標JavaScript,Dart在靜態語法方面和Java非常相似,如類...
    寒橋閱讀 6,974評論 3 11
  • 變量聲明 var 類似于 JavaScript 中的 var,它可以定義變量 最大的不同是 Dart 中 var ...
    夢幽辰閱讀 211評論 0 0
  • 前言 最近要做flutter項目,Dart語法自然是前提,于是就在Dart學習和理解的過程中做了一些適合自己的知識...
    小哥_xiaoge閱讀 425評論 0 1
  • Dart語言之數據類型 數據類型 dart語言數據類型包含一下類型:變量-Var常量-Const數值型-Numbe...
    楓葉無處漂泊閱讀 2,497評論 2 7