一般程式語言都有的基本資料型態 (Data type)

整數 (int) 浮點數 (double) 布林值 (bool) 字串 (String)

變數 Variables

沒有初始化的變數都會有預設值 null,即空值的意思
在Dart中,一切都是物件,所有的物件都是繼承自Object類別,也就是說所有的變數不管是數值、方法甚至是 null 都是物件

var:推斷資料型態後,就不能再給它其他型態的值


        void main() {
        var pi = 3.14159265359; //3.14159265359,有小數點的數值,推斷變數pi 型態為 double
        print(pi); //印出 3.14159265359

        //變數pi 已經是double 型態,若給予它不同型態的值,會報錯
        pi = "3.14159265359";//Error: A value of type 'String' can't be assigned to a variable of type 'double'.

    }

dynamic:所有物件的基礎型態,也就是說任何物件都可以是dynamic型態


    void main() {
        dynamic pi = 3.14159265359; //變數pi 型態為 dynamic,初始值為3.14159265359
        print(pi); //印出 3.14159265359

        pi = "3.14159265359"; //不會出錯,型態為 dynamic,可以給予任何型態的值
    }

常數 const & final

const比final更加嚴格,final只是要求資料在初始化後值不能變,所以final可以被指派為編譯後變數運算後得到的值,但const必須在編譯之前就是明確的資料值

Dart 的內建的資料型態有數值型 (num)、布林型 (bool)、字串 (String)、Map、List(Dart中的 Array)、Runes、Symbols

在Dart 2.3後,spread operator (...) 以及 null-aware spread operator (...?),可以插入另一個集合的資料


    main() {
        var list = [1, 2, 3];
        var list2 = [0, ...list];
        print(list2); //印出 [0, 1, 2, 3]

        var list3;
        var list4 = [0, ...?list3];
        print(list4); //印出 [0]
    }

Set的語法跟Map很像,若用未明確宣告資料型態來宣告{ }的話,Dart 會推斷型態為Map


    void main() {
        var set1 = {1, 2, 3}; // 推斷為 Set<int>
        var set2 = {}; // 推斷為 Map<dynamic, dynamic>, 不是 SET
        var set3 = <int>{}; // 為 Set<nt>
  
        set1.add(3); //新增重複的值,會被省略
        print(set1.length); //印出 3
    }

Runes
Runes是代表字串的UTF-32 code points,Unicode為每一個字元、標點符號、表情符號等都定義了一個唯一的數值,
通常用\uXXXX的方式表示Unicode code point,這裡的XXXX是4個16進位制的數。


    void main() {
        Runes heart = '\u2665';
        Runes laugh = '\u{1f600}';
        print(String.fromCharCodes(heart) + ' ' + String.fromCharCodes(laugh)); //印出 ♥ ?

        String heart2 = '\u2665';
        String laugh2 = '\u{1f600}';
        print(heart2 + ' ' + laugh2); //印出 ♥ ?
    }

Symbols


    void main() {
        //有兩種新建Symbol的方式:Symbol('name') 或 #name
        print(Symbol('a') == #a); // 印出 true, == 是相等的運算符號,後面會介紹

        var name = 'test';
        Symbol symbol = #name;
        print(symbol); //印出 Symbol("name")
        print(#name); //印出 Symbol("name")
    }

Named constructors(命名建構式)

一個類別只能有一個主建構式 Unnamed constructors,不像在Java 擁有多載(Overloading) 的特性,
是用引數來區分,但是Dart 用另一種方法來滿足這個要求,並且能更清楚表達程式,
增加易讀性,即使用 Named constructors(命名建構式)


    class Person {

        String name = "匿名";
        int age;

        //主建構式 Unnamed constructors
        //由於建構式引數賦值給物件屬性變數的情況太常見了,Dart提供了一個語法來簡化這個操作
        Person(this.name, this.age);

        //Named constructors
        Person.secretAge(this.name);

        //Named constructors
        Person.anonymous(this.age);
    }

    void main() {
        Person a = Person("Ryder",26); 
        print("a.name = ${a.name}, a.age = ${a.age}");  //印出 a.name = Ryder, a.age = 26

        Person b = Person.secretAge("Judy"); 
        print("b.name = ${b.name}, b.age = ${b.age}");  //印出 b.name = Judy, b.age = null

        Person c = Person.anonymous(65); 
        print("c.name = ${c.name}, c.age = ${c.age}");  //印出 c.name = 匿名, c.age = 65
    }

Redirecting constructors(重定向建構式)

有時候一個建構式會調動類別中的其他建構式,一個重定向建構式是沒有程式碼的,在建構式聲明後,使用冒號呼叫其他建構式


    vclass Point {
        num x;
        num y;

        // 主建構式
        Point(this.x, this.y);

        // 呼叫主建構式
        Point.alongXAxis(num x) : this(x, 0);
    }

函數 - Optional Named parameters & Optional positional parameters


    //Optional Named parameters
    void enableFlags({bool bold, bool hidden}) {...}
    enableFlags(bold: true, hidden: false);

    //Optional positional parameters
    void enableFlags([bool bold, bool hidden]) {...}
    enableFlags(true, false);

匿名函數:有時候也被稱為lambda或者clourse閉包


    語法:

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

    or

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

Lexical closures(詞法閉包)

Closure閉包,是一個方法物件,不管該物件在何處被呼叫,該物件都可以使用其作用域內的變數,方法可以封閉定義到其作用域內的變數。


    Function makeAdder(num addBy) {
        return (num i) => addBy + i;
    }

    main() {
        var add2 = makeAdder(2);
        // add2 為一個匿名方法物件 (num i) => 2 + i;

        var add4 = makeAdder(4);
        // add4 為一個匿名方法物件 (num i) => 4 + i;

        print(add2(3)); //印出 5
        print(add4(3)); //印出 7
    }

類別 : 多型

會限制物件使用的方法數量,只有物件的宣告類別裡存在的方法才能使用

編譯時,物件使用方法前會去宣告此物件的類別,檢查是否有此方法
執行時,則以new的類別方法執行
多型new 出來的物件如果要用子類別的方法,需要在父類別新增一個同名稱同引數的方法,來給子類別override

物件轉型


    class Student {}

    class SocialGroupStudent extends Student {}

    class ScienceGroupStudent extends Student {}

    main() {
        Student student = new ScienceGroupStudent();
        print(student);
        //印出Instance of 'ScienceGroupStudent',為ScienceGroupStudent的例項,但宣告的類別為Student

        /*但是若直接
        ScienceGroupStudent scienceGroupStudent = student;
        會編譯失敗,當異質宣告時,宣告類別必須是實體化類別的父類別:
        父類別 物件 = new 子類別( [ 引數值 ] ) ;
        上面例子不符合規則
        */

        //當有繼承關係時,可以運用轉型 ( casting )
        ScienceGroupStudent scienceGroupStudent = student as ScienceGroupStudent;//降轉
        print(scienceGroupStudent);
        //印出Instance of 'ScienceGroupStudent',同student

        //student宣告類別為Student,與SocialGroupStudent也有繼承關係,也可以轉型,編譯時也可以成功,但是執行時會出現 Unhandled Exception: type 'ScienceGroupStudent' is not a subtype of type 'SocialGroupStudent' in type cast
        SocialGroupStudent socialGroupStudent = student as SocialGroupStudent;
        print(socialGroupStudent);
        //執行時相當於是在做
        //SocialGroupStudent socialGroupStudent = new ScienceGroupStudent();
        //故會報錯
    }
An unhandled error has occurred. Reload 🗙