【dart學習】-- Dart之類和對象

一,概述

  (Class)是面向對象程序設計,實現信息封裝的基礎。類是一種用戶定義的類型。每一個類包含數聽說明和一組操做數據或傳遞消息的函數。類的實例稱爲對象。java

      Dart的類與其它語言都有很大的區別,好比在dart的類中能夠有無數個構造函數,能夠重寫類中的操做符,有默認的構造函數,因爲dart沒有接口,因此dart的類也是接口,所以你能夠將類做爲接口來從新實現。程序員

  Dart是一門使用類和單繼承的面嚮對象語言全部的對象都是類的實例,而且全部的類都是Object的子類。

swift

二,類定義

  • 類的定義用class關鍵字
  • 若是未顯式定義構造函數,會默認一個空的構造函數
  • 類首字母必須大寫緩存

  • 使用new關鍵字和構造函數來建立對象
  • class  Person { //未定義父類的時候,默認繼承自Object
      num x;
      num y;
      num z;
    }
    
    void main(List<String> args){
        var person = new Person();//調用默認的構造函數
        person.x = 10;   //使用點()引用實例變量或方法
        person.y = 11;
        person?.z = 12; //若是p不爲空,設置它的變量y的值爲4
        print(person.x);
        print(person.y);
        print(person.z);
    }.

    結果:ide

    10
    11
    12

三, 實例變量

  • 聲明實例變量時,全部未初始化的實例變量的值爲null
  • 對象的成員包括函數和數據(分別是方法和實例變量)。使用點(.)引用實例變量或方法; 
  • 使用?.來確認前操做數不爲空, 經常使用來替代. , 避免左邊操做數爲null引起異常
  • void main(){
        var point = new Point();
        point.x = 4;  //使用點()引用實例變量或方法
    point?.y = 5;//若是p不爲空,設置它的變量y的值爲5
    print(point.x); print(point.y);
    } .
    class Point { 
    int x; // null
    int y; // null
    int z = 0; // 0
    }

四,構造函數

  • 若是你沒有聲明構造函數,默認有構造函數,默認構造函數沒有參數,調用父類的無參構造函數。子類不能繼承父類的構造函數
    class Person {
       int x;
       int y;
    }
    
    void main(List<String> args){
         var person = new Person();
    }
  • 構造函數就是一個與類同名的函數,關鍵字 this 是指當前的,只有在命名衝突時有效,不然dart會忽略處理
  • void main(){
        var point = new Point(4, 5);
    }
    class Point {
        int x;
        int y;
     //本身定義的類名構造函數
      Point(int x, int y) {
    this.x = x;
       this.y = y;
    }
    }

     

  • 在Dart中構造函數的名稱能夠是類名 ClassName  或者類名和標識符 ClassName.identifier 。 其中構造函數名稱是「ClassName」的函數叫「類名構造函數」;構造函數名稱是「ClassName.identifier」的函數叫「命名構造函數」

    (1)類名構造函數 (ClassName)函數

    import 'dart:math';
    
    class Point {
      int y;
      int x;
    
      // 類名構造函數
      Point(num x, num y) {
        this.x = x;
        this.y = y;
      }
      // .....
    }

    在構造函數裏初始化成員屬性是很常見的事情,所以Dart開發了新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成post

    class Point {
      num x, y;
      // 注意x,y的賦值會在構造函數執行以前完成.
      Point(this.x, this.y);
    }


    (2)命名構造函數(ClassName.identifie) 
      使用命名構造函數能夠爲類提供多個構造函數,按官方的說法就是提供額外的清晰度this

    class Point {
      num x, y;
    
      Point(this.x, this.y);
    
      // 命名構造函數
      Point.origin() {
        x = 0;
        y = 0;
      }
    }

      調用命名構造函數spa

    main(List<String> args) {
      // 調用命名構造函數
      Point point1 = Point.origin();
    }

          在命名構造函數裏也能夠用新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成   設計

  • class Point {
      num x, y;

    // 注意x,y的賦值會在構造函數執行以前完成.
      Point(this.x, this.y);

  • (2)命名構造函數(ClassName.identifie) 

      使用命名構造函數能夠爲類提供多個構造函數,按官方的說法就是提供額外的清晰度

    class Point {
    num x, y;
    
    Point(this.x, this.y);
    
    // 命名構造函數
    Point.origin() {
    x = 0;
    y = 0;
    }
    }

      調用命名構造函數

    main(List<String> args) {
    // 調用命名構造函數
    Point point1 = Point.origin();
    }

      在命名構造函數裏也能夠用新的語法糖來簡化這種操做,好比將Point的類名構造構造函數改寫成

    class Point {
      num x, y;
      //類名構造函數
       Point(this.x, this.y);
       // 命名構造函數
       Point.origin(this.x,this.y);
    }
    void main(List<String> args){
      var point = new Point.Orgin(1,2);
      print(point.x);
      print(point.y);
    }

    (3)默認構造函數(前面我咱們已經說了,咱們放在這裏再提一下,方便區分)
       若是類中沒有聲明構造函數,Dart會提供一個默認的構造函數。這個默認的構造函數會調用父類的默認構造函數,而且該構造函數是沒有參數的。

    class Person {
       int x;
       int y;
    }
    
    void main(List<String> args){
         var person = new Person();
    }

     

  • Dart的第一個版本實例化對象須要new關鍵字,但在Dart 2以後就去掉了new關鍵字

    main(List<String> args) {
      // 調用類名構造函數
      Point point1 = Point(3,4);
      print(point1.x);
    }
  • 調用父類非默認的構造函數(類比下面的重定向理解記憶)

    在默認狀況下,子類能夠調用父類的未命名,無參數的構造函數即默認構造函數。父類的構造函數會在子類的構造函數以前開始調用,若是子類中存在須要初始化的成員屬性,則能夠先初始化子類成員屬性,再調用父類的構造函數,執行過程以下

    1. 初始化子類成員屬性
    2. 調用父類構造函數
    3. 子類構造函數

    若是父類中沒有默認的構造函數,你必須手動調用父類的構造函數,在子類的構造函數體以前經過 : 指定調用父類構造函數,示例以下

    // Person類中沒有一個無參數,未命名的構造函數
    class Person {
      String firstName;
      // 命名構造函數
      Person.fromJson(Map data) {
        print('in Person');
      }
    }
    
    class Employee extends Person {
      // 你必須調用父類的super.fromJson(data).
      Employee.fromJson(Map data) : super.fromJson(data) {
        print('in Employee');
      }
    }
    
    main() {
      var emp = new Employee.fromJson({});
    }

 

  • 重定向構造函數 (在這裏 :有從新指向的含義)

    有時構造函數的惟一目的是重定向到同一類中的另外一個構造函數重定向構造函數的主體爲空構造函數調用出如今冒號( :)以後 。 大意就是在建立類時,我定義一個命名構造函數,可是這個構造函式的主體我不實現。直接經過:另一個構造函數。實現對外界傳入的參數接收並賦值給內部的變量。

    class Point {
      num x, y;
      //類名構造函數
      Point(this.x, this.y);
      // 命名構造函數
     Point.order(this.x,this.y);
     Point.origin(num a,num b):this.order(a,b);  //重定向構造函數, origin構造函數將外界的傳值,指向給了order構造函數。
    } 
    void main(List<String> args){ 
       var point = new Point.origin(1,2); 
       print(point.x); 
       print(point.y); 
    }
  • 常量構造函數 
    若是你的類建立的對象從不改變,你能夠建立一些編譯時的常量對象。所以,定義一個const構造函數,且保證全部的對象變量都是final。
    class ImmutablePoint {
      static final ImmutablePoint origin = const ImmutablePoint(0, 0);
      final num x, y;
      const ImmutablePoint(this.x, this.y);
    }
  • 工廠構造函數

    在實現一個構造函數時使用factory關鍵字,該構造函數並不老是建立其類的新實例。例如,工廠構造函數可能會從緩存中返回實例,也可能會返回子類型的實例。

    class Logger {
      final String name;
      bool mute = false;
    
     // _cache是私有變量
     //_在名稱前,表示該變量爲私有
      static final Map<String, Logger> _cache = <String, Logger>{};
    
      factory Logger(String name) {
        if (_cache.containsKey(name)) {
          return _cache[name];
         } else {
           final logger = Logger._internal(name);
           _cache[name] = logger;
           return logger;
        }
      }
    
      Logger._internal(this.name);
    void log(String msg) {
        if (!mute) print(msg);
      }
    }
    注意:工廠構造者對this沒有訪問權限。

    像調用任何其餘構造函數同樣調用工廠構造函數:

    var logger = Logger('UI');
    logger.log('Button clicked');

五,方法

      方法是爲對象提供行爲的函數。  

  • Getters和Setters 方法
     (1)Getters和setters是讀取和修改對象的特定方法,每次調用對象的屬性時,Dart都會隱式的調用一次getter方法,這容許你能夠在修改或者讀取對象屬時作一些操做。
     (2)經過 get和 set 關鍵詞重寫對象的默認行爲。
    class Rectangle {
      num left, top, width, height;
      //類名構造函數
      Rectangle(this.left, this.top, this.width, this.height);
      // 重寫right屬性(類比oc記憶,這裏多了一個set和get的關鍵字)
      num get right => left + width;
      set right(num value) => left = value - width;
      num get bottom => top + height;
      set bottom(num value) => top = value - height;
    }
    
    void main() {
      var rect = Rectangle(3, 4, 20, 15);
      assert(rect.left == 3);
      rect.right = 12;
      assert(rect.left == -8);
    }
  • 實例方法
    在對象的實例方法有權限獲取對象變量和this,在接下來的例子裏distanceTo就是一個對象方法:

    import 'dart:math';
    class Point {
      num x, y;
      Point(this.x, this.y);
      num distanceTo(Point other) {
        var dx = x - other.x;
        var dy = y - other.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
  • 抽象方法

    (1) 實例的getter和setter方法就是抽象的方法,定義一個接口,但將其實現留給其餘類。
    (2)抽象方法使用分號 ; 而不是方法體
    (3)抽象方法只存在於抽象類中。

    abstract class Doer {
      //...定義實例變量和方法...
    
      //定義一個抽象方法
      void doSomething();
    }
    
    class EffectiveDoer extends Doer {
      void doSomething() {
       //...實現一個抽象方法...
       }
    }

六,抽象類和接口

  • 抽象類
    使用 abstract 修改器能夠定義一個抽象類。抽象類是不能被實例化的,但對於定義接口是很是有用的,若是你想實例化抽象類,你必須實現抽象類,才能被實例化

  • // 此對象愛你過是抽象類,所以不能被實例化
    abstract class AbstractContainer {
      // 定義構造函數、字段、方法...
    
      void updateChildren(); // 抽象方法
    }
  • 隱式的接口
    每一個類都是都是隱式的接口,包括類的方法和屬性。若是你想建立一個類A不繼承B的實現,能夠實現B的接口來建立類A。一個類容許經過implements 關鍵詞能夠實現多個接口

    // 每一個類都是一個隱式的接口,因此Person類也是個接口,包括成員屬性和方法.
    class Person {
      // 可在接口中實現, 但僅對這個庫可見.
      final _name;
    
      // 構造函數不可以被接口實現
      Person(this._name);
    
      // 可在接口中實現.
      String greet(String who) => 'Hello, $who. I am $_name.';
    }
    
    // 實現Person接口.
    class Impostor implements Person {
      get _name => '';
    
      String greet(String who) => 'Hi $who. Do you know who I am?';
    }
    
    String greetBob(Person person) => person.greet('Bob');
    
    void main() {
      print(greetBob(Person('Kathy')));
      print(greetBob(Impostor()));
    }

    實現多個接口

    class Point implements Comparable, Location {...}

七,類的繼承

  • 使用extends建立子類,super引用父類,子類能夠重寫實例方法、getter和setter,使用@override註釋重寫,使用@proxy註釋來忽略警告
    class Television {
        void turnOn() {
            _illuminateDisplay();
            _activateIrSensor();
        }
    }
    
    class SmartTelevision extends Television {
        void turnOn();
        _bootNetworkInterface();
        _initializeMemory();
        _upgradeApps();
    }
  • 重寫成員

    可使用 @override 關鍵字,子類能夠重寫實例的方法,getters和setters

    class SmartTelevision extends Television {
      @override
      void turnOn() {...}
      // ···
    }
  • 重寫操做符

    你能夠重寫如下表中顯示的運算符。例如,若是定義Vecor類,則能夠定義+方法來添加兩個vectors。
    注意:
      你可能已經注意到了!=不是可重寫的運算符。表達式E1!= E2只是語法上的糖!( E1 = = E2 )

    <    +    | [] >    /    ^    []=
    <=    ~/    &    ~
    >=    *    <<    ==%    
  • 能夠重寫操做符,下面是重寫加減操做符的示例

    class Vector {
      final int x, y;
    
      Vector(this.x, this.y);
    
      Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
      Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
    
      // Operator == and hashCode not shown. For details, see note below.
      // ···
    }
    
    void main() {
      final v = Vector(2, 3);
      final w = Vector(2, 2);
    
      assert(v + w == Vector(4, 5));
      assert(v - w == Vector(0, 1));
    }

    若是你須要重寫 == 操做符,請參考操做符教程

  • noSuchMethod()
    當用戶調用你定義的類中不存在的屬性與方法時,能夠作出一些響應,經過重寫noSuchMethod()

    class A {
      @override
      void noSuchMethod(Invocation invocation) { print('You tried to use a non-existent member: ' + '${invocation.memberName}'); } }

     

八,類變量和類方法  

  使用static關鍵字實現類範圍的變量和方法。  

  • 靜態變量
    靜態變量(類變量)對於類範圍的狀態和常量很是有用:
    靜態變量在使用以前不會初始化。
    class Queue {
      static const initialCapacity = 16;
      // ···
    }
    
    void main() {
      assert(Queue.initialCapacity == 16);
    }
  • 靜態方法

    靜態方法(類方法)不能在實例操做,所以它沒有訪問this的權限。

    import 'dart:math';
    
    class Point {
      num x, y;
      Point(this.x, this.y);
      static num distanceBetween(Point a, Point b) {
       var dx = a.x - b.x;
       var dy = a.y - b.y;
       return sqrt(dx * dx + dy * dy);
      }
    }
    
    void main() {
     var a = Point(2, 2);
     var b = Point(4, 4);
     var distance = Point.distanceBetween(a, b);
     assert(2.8 < distance && distance < 2.9);
     print(distance);
    }

    注意:
     (1)爲了經常使用或普遍使用的實用程序和功能,考慮使用頂層函數,而不是靜態方法。

    import 'dart:math';
    
    class Point {
     num x, y;
     Point(this.x, this.y);
    }
    // 頂級函數
     num distanceBetween(Point a, Point b) {
     var dx = a.x - b.x;
     var dy = a.y - b.y;
     return sqrt(dx * dx + dy * dy);
    }
    
    
     void main() {
     var a = Point(2, 2);
     var b = Point(4, 4);
     var distance = distanceBetween(a, b);
     assert(2.8 < distance && distance < 2.9);
     print(distance);
    }

    (2)可使用靜態方法做爲編譯時常量。例如,能夠將靜態方法做爲參數傳遞給常量構造函數。

九,訪問控制

  • 默認類中的全部屬性和方法是public的。在dart中,能夠在屬性和方法名前添加「_」使私有化。如今讓咱們使name屬性私有化。
    main(List<String> args) {
      Dog d = new Dog('Duffy', 5);
      print(d.name);  //This will throw error
    }
     
    class Dog {
      String _name; //私有的變量
      int age; //公有變量
     //類名構造函數
      Dog(this.name, this.age);
     //setter && getter 方法 
      String get respectedName {
        return 'Mr.$name';
      }
    set respectedName(String newName) {
        name = newName;
      }
     //命名構造函數
      Dog.newBorn() {
        name = 'Doggy';
        age = 0;
      }
     //公有實例方法
      bark() {
        print('Bow Wow');
      }
     //私有的實例方法
      _hiddenMethod() {
        print('I can only be called internally!');
      }
    }

十,枚舉類型

  枚舉類型,一般稱爲枚舉,是一種特殊類型的類,用於表示固定數量的常量值。

  • 使用枚舉
    使用enum關鍵詞來聲明一個枚舉類型

    enum Color { red, green, blue }

    枚舉中的每一個值都有一個index索引,它返回枚舉聲明中值的從零開始的位置。例如,第一個值具備索引0,第二個值具備索引1。

    assert(Color.red.index == 0);
    assert(Color.green.index == 1);
    assert(Color.blue.index == 2);

    若要獲取枚舉中全部值的列表,請使用枚舉的values常量。

    List<Color> colors = Color.values;
    assert(colors[2] == Color.blue);

    你能夠在switch語句中使用枚舉,若是不處理枚舉的全部值,將會收到警告:

    var aColor = Color.blue;
    
    switch (aColor) {
      case Color.red:
        print('Red as roses!');
        break;
      case Color.green:
        print('Green as grass!');
        break;
      default: // 沒有default,將會報錯
        print(aColor); // 'Color.blue'
    }

    枚舉類型有如下限制:

    • 你不能子類化、混合或實現枚舉。
    • 不能顯式實例化枚舉。

 十一,泛型

  • 泛型是程序設計語言的一種特性。容許程序員在強類型程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須做出指明。
  • 從字面的意思理解來看,泛型,泛就是模糊、暫不肯定暫定的意思。能夠這樣理解,使用泛型就是,定義的一個類型,類型暫不肯定,給使用給一個佔位符給代替,在使用的時候能夠給肯定其定義的類型。
  • 泛型,基本上強類型語言都支持泛型,好比java,Oc,swift 因此Dart也不例外
  • dart全面支持泛型。假設你想在你定義的類中,想持有任意類型的數據。以下是怎樣使用泛型定義這樣的類。
    main(List<String> args) {
      DataHolder<String> dataHolder = new DataHolder('Some data');
      print(dataHolder.getData());
      dataHolder.setData('New Data');
      print(dataHolder.getData());
    }
     
    class DataHolder<T> {
      T data;
     
      DataHolder(this.data);
     
      getData() {
        return data;
      }
     
      setData(data) {
        this.data = data;
      }
    } 
相關文章
相關標籤/搜索