Flutter學習筆記(8)--Dart面向對象

来源:https://www.cnblogs.com/upwgh/archive/2019/07/19/11198535.html
-Advertisement-
Play Games

Dart作為高級語言,支持面向對象的很多特性,並且支持基於mixin的繼承方式,基於mixin的繼承方式是指:一個類可以繼承自多個父類,相當於其他語言里的多繼承。所有的類都有同一個基類Object,這和特性類似於Java語言,Java所有的類也都是繼承自Object,也就是說一切皆對象。 ...


如需轉載,請註明出處:Flutter學習筆記(8)--Dart面向對象

 

Dart作為高級語言,支持面向對象的很多特性,並且支持基於mixin的繼承方式,基於mixin的繼承方式是指:一個類可以繼承自多個父類,相當於其他語言里的多繼承。所有的類都有同一個基類Object,這和特性類似於Java語言,Java所有的類也都是繼承自Object,也就是說一切皆對象。

//實例化了一個User類的對象user
var user = new User('張三',20);
  • 實例化成員變數

Class User{
    String name;//name 成員變數
    int age;//age 成員變數
}

類定義中所有的變數都會隱式的定義setter方法,針對非空的變數會額外增加getter方法。實例化成員變數請參考如下代碼:

main(){
    var user = new User();
    user.name = '張三';//相當於使用了name的setter方法
    user.age = 25;
}
  • 構造函數

  1.常規的構造函數

構造函數是用來構造當前類的函數,是一種特殊的函數,函數名稱必須要和類名相同才行,如下代碼為User類添加了一個構造函數,函數里給User類的兩個成員變數初始化了值:

Class User{
    String name;
    int age;
    User(String mName,int mAge){
        this.name = mAge;
        this.age = mAge;
    }
}

this關鍵字指向了當前類的實例,上面的代碼可以簡化為:

Class User{
    String name;
    int age;

    User(this.name,this.age);
}

第一種沒有簡化的構造方法初始化成員變數是在方法體內進行初始化的,第二種簡化的構造方法初始化成員變數,是在實例化類的時候直接進行賦值初始化的。

  2.命名的構造函數

使用命名構造函數從另一類或現有的數據中快速實現構造函數,代碼如下所示:

Class User{
    String name;
    int age;
    //普通構造函數
    User(this.name,this.age);

    //命名構造函數
    User.fromJson(Map json){
        name = json['name'];
        age = json['age'];
    }
}

//在實例化類的時候,如果沒有傳參,會預設調用無參數的構造方法
//普通構造函數
var user = new User('張三',25);

//命名構造函數
var user = new User.fromJson(mMapJson);

我對命名構造函數的理解就是起了個名字,在Java裡面,相同參數個數的構造方法只能有一個,在Dart裡面也不例外,那麼如果我們想有多個相同參數個數的構造方法要怎麼做呢,這時候命名構造方法就顯現出他價值了,我們可以給相同參數個數的構造方法起不同的名字,這種就不會有錯誤了。

  3.子類的創建

註1:子類在繼承父類的時候,如果在父類中有顯示的提供一個無名、無參的構造函數,不會繼承父類無名有參構造函數和命名構造函數,即:子類只會繼承父類無名無參的構造函數。(程式會給類隱式的生成一個無名、無參的構造函數)

註2:子類在繼承父類的時候,如果在父類中沒有有顯示的提供一個無名、無參的構造函數,子類必須手動調用父類的一個構造函數,在這種情況下,調用的父類的構造函數要放在子類構造函數之後,在子類構造函數體之前,用“:”分隔。

註3:父類的構造函數會在子類的構造函數前調用。

註4:預設情況下,子類只能調用父類無名、無參數的構造函數

下麵我會用代碼給大家解釋上面的“註”(開始我也不會,也是上網查資料理解的!!!)

註1和註3:父類中有一個無名、無參的構造函數,子類繼承父類,會預設繼承父類無名、無參的構造函數(即使有其他無名、有參的構造函數或者命名構造函數,子類都不會調用),並且,父類的無名、無參的構造函數會在子類的構造函數之前被調用。

Class Futher {

    //無名、無參的構造函數
    Futher(){
        print('我是父類無名、無參的構造函數');
    }
}

Class Son extends Futher {
    //因為父類有顯式的聲明一個無名、無參的構造函數,所以不用手動調用父類的構造函數。
    Son.fromJson(Map mMapJson){
        print('我是子類的命名構造函數');
    }
}

var son = new Son.fromJson(mMapJson);
//列印結果
//我是父類無名、無參的構造函數
//我是子類的命名構造函

註2:下麵代碼里,子類的命名構造方法寫了兩種方式,第一種是正確的,第二種是錯誤的,有詳細的註釋, 如果有疑問請留言。

Class Futher {

    //無名、無參的構造函數
    Futher.printSth(){
        print('我是父類無名、無參的構造函數');
    }
}

Class Son extends Futher {
    //因為父類沒有有顯式的聲明一個無名、無參的構造函數,所以需要手動的調用父類的構造函數。
    Son.fromJson(Map mMapJson) : super Futher.printSth{
        print('我是子類的命名構造函數');
    }

    //這種寫法會報錯,因為父類中沒有顯示的提供一個無名、無參的構造函數。所以需要像上面那樣,手動調用父類的一個構造函數
    Son.fromJson(Map mMapJson){
        print('我是子類的命名構造函數');
    }
}

  4.構造函數初始化列表

上面在講解常規的構造函數和命名構造函數的時候,示例代碼都有對類中的成員變數進行了初始化,特點是在構造函數的方法體內進行初始化,初始化成員變數還有另一種方式,就是在構造函數運行前來初始化成員變數。

Class User {
    String name;
    int age;

    User(mName,mAge)
        :name = mName,
        age = mAge{
            // Do Some Thing
        }
}

特點是在構造函數的方法體前(大括弧前面)來初始化成員變數,變數間用“,”分隔。

  • 讀取和寫入對象

get()和set()方法是專門用於讀取和寫入對象的屬性的方法,每一個類的實例,系統都會隱式的包含有get()和set()方法。

例如,定義一個矩形的類,有上、下、左、右:top、bottom、left、right四個成員變數,使用get及set關鍵字分別對right、bottom進行獲取和設置值。代碼如下所示:

Class Rectangle {
    num left;
    num top;
    num width;
    num height;

    Rectangle(this.left,this.top,this.width,this.height);

    num get right => left + width;//獲取righht的值(第一行)

    set right(num value) => left = value - width;//設置right的值,同時left也發生了變化(第二行)

    num get bottom => top + height;//獲取bottom的值(第三行)

    set bottom(num value) => top = value - height;//設置bottom值,同時top也發生了變化(第四行)
}

main(){
    var rect = new Rectangle(3,4,20,15);//實例化Rectangle,並給類中的4個變數進行初始化賦值

    print('left:'+rect.left.toString());//獲取left的值,並列印 left = 3
    print('right:'+rect.right.toString());//獲取right的值,並列印,這裡執行了Rectangle類中第一行代碼,right = left + width,right = 3+20 = 23
    rect.right = 30;//重新給right進行賦值 right = 30,這裡執行了Rectabgke類中的第二行代碼,將right的值設置為30,並且,將left的值改為30 - 20,left = 30-20 = 10
    print('right的值改為30');
    print('left:'+rect.left.toString());//獲取left的值,並列印,因為上面給right重新賦值的時候,也改變了left的值,所以,此時left = 10
    print('right:'+rect.right.toString());//rect.right = 30將right的值改為了30,所以,right = 30


    print('top:'+rect.top.toString());
    print('bottom:'+rect.bottom.toString());
    rect.bottom = 50;
    print('bottom的值改為50');
    print('top:'+rect.top.toString());
    print('bottom:'+rect.bottom.toString());
}

//列印結果
left:3
right:23
right的值改為30
left:10
right:30
top:4
bottom:19
bottom的值改為50
top:35
bottom:50

上面的示例註釋已經解釋的很清楚了,如果有任何疑問,請留言!!!

這裡我就解釋一下“=>”的作用,在Dart裡面,大家可以簡單的理解為接下來要繼續執行後面的操作。

  • 重運算符載操作

在講解重載運算符前需要先說明Dart裡面的一個關鍵字operator,operator和運算符一起使用,表示一個運算符重載函數,在理解時可以將operator和運算符(如operator+或operator-)視為一個函數名。編寫一個例子方便理解。

Class Vector {
    final int x;
    final int y;
    const Vector(this.x,this.y);

    //重載加號 + (a+b)
    Vector operator + (Vector v){
        return new Vector(x + v.x,y + v.y);
    }
}

main() {
    //實例化兩個變數
    final result1 = new Vector(10,20);
    final result2 = new Vector(30,40);

    final result = result1 + result2;

    print('result.x = '+result.x.toString()+'',+'result.y = '+result.y.toString());

    //列印結果
    result.x = 40,result.y = 60
}

 

講解一下上面的例子,如果有不對的地方,麻煩留言指出。

首先創建了一個Vector類,聲明兩個成員變數x和y還有一個構造方法,在Vector類裡面重載一個加法運算符,重載操作返回Vector對象,接下來在main函數裡面,實例化了兩個Vector變數,兩次操作分別給

x和y進行了賦值,x = 10;y = 20;x = 30;y = 40。然後讓result1和result2這兩個變數相加,看到這裡大家可能會有疑問,兩個對象變數怎麼相加呢?這裡我們的運算符重載就發揮出作用了,實際上,在執行final result = result1 + result2;這行代碼的時候,其實是對象result1調用了"operator +"這個函數,並將result2這個對象當作一個參數傳遞給了這個函數,從而實現了對象result1中的x和對象result2中的x相加,對象result1中的y和對象result2中的y相加的操作,所以最終列印的結果result.x = 40,result.y = 60。

看到這裡,大家是否對運算符重載有了一定的理解了呢?如果有任何疑問,歡迎留言提問!!!

註:對於 Dart 提供的所有操作符,通常只支持對於基本數據類型和標準庫中提供的類的操作,而對於用戶自己定義的類,如果想要通過該操作符實現一些基本操作(比如比較大小,判斷是否相等),就需要用戶自己來定義關於這個操作符的具體實現了。

  •  繼承類

繼承是面向對象編程技術的一塊基石,因為它允許創建分等級層次的類。繼承就是子類繼承父類的特征和行為,使得子類對象具有父類的實例域和方法;或子類從父類繼承方法,使得子類具有父類相同的行為。Dart裡面使用extends關鍵字來實現繼承,super關鍵字來指定父類。

Class Animal {
    void eat(){
        print('動物會吃');
    }

    void run(){
        print('動物會跑');
    }
}

Class Human extends Animal {
    void say(){
        print('人會說');
    }

    void study(){
        print('人會學習');
    }
}

main(){
    var animal = new Animal();
    animal.eat();
    animal.run();

    value human = new Human();
    human.eat();
    human.run();
    human.say();
    human.study();

    //列印結果
    動物會吃
    動物會跑

    動物會吃
    動物會跑
    人會說
    人會學習
}

 

  • 抽象類

抽象類類似於Java語言中的介面。抽象類里不具體實現方法,只是寫好定義介面,具體實現留著調用的人去實現。抽象類可以使用abstract關鍵字定義類。

  1. 抽象類通過abstract關鍵字來定義。
  2. Dart中的抽象方法不能用abstract聲明,Dart中沒有方法體的方法我們成為抽象方法。
  3. 如果子類繼承了抽象類,就必須實現裡面的抽象方法。
  4. 如果把抽象類當作介面實現的話,就必須得實現抽象類裡面的所有屬性和方法。
  5. 抽象類不能實例化,只有繼承它的子類可以實例化。
abstract class Animal{
    eat();   //抽象方法
    run();  //抽象方法  
    printInfo(){
    print('我是一個抽象類裡面的普通方法');
  }
}

class Dog extends Animal{
    @override
    eat() {
        print('小狗在吃骨頭');
    }

    @override
    run() {
        // TODO: implement run
        print('小狗在跑');
    }  
}
class Cat extends Animal{
    @override
    eat() {
        // TODO: implement eat
        print('小貓在吃老鼠');
    }

    @override
    run() {
        // TODO: implement run
        print('小貓在跑');
    }

}

main(){
    Dog d=new Dog();
    d.eat();
    d.printInfo();

    Cat c=new Cat();
    c.eat();
    c.printInfo();


    // Animal a=new Animal();   //抽象類沒法直接被實例化
}
  • 枚舉類型

枚舉類型是一種特殊的類,通常用來表示相同類型的一組常量值,用enum來定義,每個枚舉類型都有一個index和getter,index用來標記元素的元素位置。第一個枚舉元素的索引是0,枚舉不能被繼承,不能創建實例。

//定義一個枚舉類
enum Color {
    red,
    green,
    blue
}

//列印枚舉類中green的索引
print(Color.green.index); // 1

//獲取枚舉類中所有的值,使用value常數
List<Color> colorList = Color.values;

 

因為枚舉類中的每個元素都是相同類型,所以可以使用switch語句來針對不同的值做不同的處理,示例代碼如下:

//定義一個枚舉類
enum Color {
    red,
    green,
    blue
}

main(){
    Color mColor = Color.blue;

    switch (mColor){
        case Color.red:
        print('紅色');
        break;
        
        case Color.green:
        print('綠色');
        break;
        
        case Color.blue:
        print('藍色');
        break;

        default:
        break;
    }

    //列印結果
    藍色
}

 

  • Mixins

Mixins(混入功能)相當於多繼承,也就是說可以繼承多個類,使用with關鍵字來實現Mixins的功能,示例代碼如下:

Class First {
    void printSth(){
        print('im first printSth');
    };
}

Class Second {
    void printSth(){
        print('im Second printSth');
    };

    void secondPrint(){
        print('test');
    }
}

Class A = Second with First;

main (){
    A a = new A();
    a.printSth();
    a.secondPrint();

    //列印結果
    im first printSth
    test
}

 

這段示例代碼,是將類First混入到了Second類中並給了類A,這塊我在學習的時候有個疑問,同樣的printSth方法,將First混入到Second後,列印的是First中的im first printSth,這是因為First中的printSth方法將Second中的printSth方法覆蓋了嘛?

  • 泛型

泛型通常是為了類型安全而設計的,適當的指定泛型類型會生成更好的代碼,可以使用泛型來減少代碼重覆,Dart中使用<T>的方式來定義泛型。例如,如果想要List只包含字元串,可以將其聲明為list<String>。如下所示:

var names = new List<String>();
names.addAll(['張三','李四','王五']);

泛型用於List和Map類型參數化:

List:<type>
Map:<keyType,valueType>

var names = new List<String>['張三','李四','王五'];
var weeks = new Map<String,String>{
    'Monday’' : '星期一',
    'Tuesday' : '星期二',
    'Wednesday' : '星期三',
    'Thursday' : '星期四',
    'Friday' : '星期五',
    'Saturday' : '星期六',  
};

 

  • 庫的使用

  1.引用庫

通過import語句在一個庫中引用另一個庫的文件,需要註意一下事項:

1.1在import語句後面需要接上庫文件的路徑。
1.2對Dart語言提供的庫文件使用dart:xx格式
1.3第三方的庫文件使用package:xx格式

import的例子如下:

import 'dart:io';
import 'package:mylib/mylib.dart';

 

  2.指定一個庫的首碼

當引用的庫擁有相互衝突的名字,可以為其中一個或幾個指定不一樣的首碼,這與命名空間的概念比較接近,示例代碼如下:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

Element emelent1 = new Element();//預設使用lib1裡面的Element
lib2.Element emelent2 = new lib2.Element();//使用lib2裡面的Element

 

lib1/lib1.dartlib2/lib2.dart裡面都有Element類,如果直接引用就不知道具體引用哪個Element類,所以代碼中把lib2/lib2.dart指定成lib2,這樣使用lib2.Element就不會發生衝突。  3.引用庫的一部分如果只需要使用庫的一部分內容,可以有選擇的進行引用,有如下關鍵字:

3.1show關鍵字:只引用一點
3.2hide:除此之外都引用

示例代碼如下:

//導入foo
import 'package:lib1/lib1.dart' show foo;

//除了foo導入其他所有內容
import 'package:lib1/lib1.dart' hide foo;

 

  • 非同步支持

非同步支持參考文檔

  • 元數據

使用元數據給代碼添加更多的信息,元數據是以@開始的修飾符,在@後面接著編譯時的常量或調用一個常量的構造函數。目前Dart語言提供了三個@修飾符:

  1.@deprecated 被棄用的

  2.@override 重寫

  3.proxy 代理

使用@override修飾符可以重寫父類的方法。人類重寫eat方法,代碼如下所示:

//動物類
Class Animal {
    void eat(){
        print('動物會吃');
    }
    void run(){
        print('動物會跑');
    }
}

//人類
Class Human extends Animal {
    void say(){
        print('人會說');
    }

    @override
    void eat(){
        print('人類也會吃的哦');
    }
}

main(){
    var animal = new Animal();
    animal.eat();
    animal.run();

    value human = new Human();
    human.say();
    human.eat();

    //列印結果
    動物會吃
    動物會跑
    人會說
    人類也會吃的哦
}

 

元數據可以修飾library(庫)、class(類)、typedef(類型定義)、type parameter(類型參數)、constructor(構造函數)、factory(工廠函數)、function(函數)、field(作用域)、parameter(參數)、variable declaration(變數聲明)。

下一章節:Flutter學習筆記(9)--組件Widget


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Oracle中分頁和MySql中的分頁不同,MySql中的分頁使用關鍵字limit即可,相對簡單一點,Oracle中分頁使用rownum ,相對複雜一點。 sql語句如下: Oracle中rownum是虛擬列,是得出結果後,再進行計算的。所以,只能是小於的,無法大於,要使用的大於,就必須使用別名,如 ...
  • 對於oracle資料庫下的企業級應用開發,經常會使用到新建用戶,新建表空間以及數據的遷移工作。雖然目前互聯網存在很多單個問題的解決方案,但是比較零散,本博文結合研發兄弟們的實際現狀,提供一套完整初始研發資料庫環境的搭建手段,免得導出搜素。 登錄相關操作 預設有以下2個超級用戶: sys:超級管理員用 ...
  • 背景 1. SQLSERVER資料庫中單表數據幾十億,分區方案也已經無法查詢出結果。故:採用導出功能,導出數據到Text文本(文本 40G)中。 2. 因上原因,所以本次的實驗樣本為:【數據量:61w條,文本大小:74M】 選擇DataX原因 1. 試圖維持統一的異構數據源同步方案。(其實行不通) ...
  • 配置前先把功能變數名稱映射配好哈 詳情參考我的其他隨筆 下載好hdfs.tar.gz 後 在/home/ldy下 mkdir apps/ tar -xzvf hdfs.tar.gz -C /home/ldy/apps/ #專門用來安裝hdfs 和jdk的 修改環境變數:vim /etc/profile 在最 ...
  • 初識事務隔離 事務隔離級別的出現都是針對資料庫的具體問題的, SQL 92標準對事務併發處理會存在的異常情況進行了分級, 分別為臟讀(Dirty Read)、不可重覆讀(Unrepeatable Read)和幻讀(Phantom Read). 三種異常 舉個例子, 有個heros_temp表, 中有 ...
  • 一、基於詞項與全文的搜索 1、詞項 Term(詞項)是表達語意的最小單位,搜索和利用統計語言模型進行自然語言處理都需要處理Term。 Term的使用說明: 1)Term Level Query:Term Query、Range Query、Exists Query、Prefix Query、Wild ...
  • 轉自:https://blog.csdn.net/weixin_33979203/article/details/87621768 改進,再找不到key,返回''值,之前的是在找不到的情況下,查找到第一的值。 ...
  • 在使用eclipse連接mysql資料庫時報異常: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) at com.mysql.jdbc.SQLError.createSQLE ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...