官术网_书友最值得收藏!

3.2 Dart中的類

Dart是一門基于類和繼承的面向對象語言,講到對象,我們就不得不談類。對象是類的實例,對象也是由類構造出來的,更通俗地講,我們可以把類理解為對象的模板,在類中定義了對象的屬性和方法。

3.2.1 自定義類與構造方法

我們之前在Dart中使用的任何數據類型其實都是類,包括描述整數的int類、描述浮點數的double類、描述字符串的string類等。在開發中,我們可以根據實際需要定義自己的類,在Dart中,使用class關鍵字進行類的定義,示例如下:

上面我們簡單定義了一個圓形類,其中定義了3個屬性,分別描述圓的半徑和圓心的X、Y坐標。盡管我們沒有給這個圓形類定義任何方法,但它已經是一個完整的自定義類了,我們可以通過它構造圓形對象來存儲數據。

通過類創建對象需要調用類的構造方法,類的構造方法通常與類名一致,也可以定義獨立名稱的構造方法,但是依然需要通過類名來調用。當我們定義完一個類時,Dart默認會生成一個沒有參數的構造方法,我們可以直接通過類名進行調用,例如:

類實例中封裝的屬性可以通過點語法來進行訪問,可以進行屬性值的設置,也可以進行屬性值的讀取。在默認情況下,如果沒有對實例的某個屬性進行過賦值,此屬性的值就為null。但是,在進行類的定義時,也可以為屬性提供默認值,例如:

構造方法是類的實例對象生成過程中的重中之重。Dart默認提供的構造方法雖然可以完整地構造出對象,但是其中定義的屬性都為null值或默認值。我們也可以通過實現構造方法來干預對象生成的過程,例如:

如上所示,重寫的構造方法中定義了3個參數,分別對應圓形的半徑和圓心點X、Y坐標。在使用Circle類構造對象時,需要將指定的參數傳入,例如:

    var circle = new Circle(6,1,1);//使用參數構造圓形對象

有一點需要額外注意,一旦重寫了構造方法,默認的無參構造方法將不再可用。在上面的構造方法中,this關鍵字指的就是當前實例對象,構造方法的實質是將對象屬性的賦值過程由外界封裝到類的內部。

在Dart中,關于構造方法的編寫還有一個小技巧,一般情況下,我們可以直接將構造方法定義成如下模樣,Dart會自動進行參數和屬性的匹配,進行賦值,非常方便。

有時,一個類需要有多個構造方法,比如自定義的圓形類,很多時候需要快速創建出單位圓(圓心為坐標原點、半徑為1的圓)。這時,就可以定義一個便捷的構造方法幫助我們直接生成單位圓,這類構造方法也被稱為命名構造方法,示例如下:

命名構造方法通常用來快速地創建標準對象,同樣,命名構造方法也可以有參數,并且只要參數名與類中定義的屬性名一致,也可以使用Dart自動匹配賦值的特性。

在Dart中,類還有一個強大的功能是支持繼承,關于繼承的內容后面會詳細介紹,但是這里你需要牢記,構造方法不會被繼承。

3.2.2 實例方法

類封裝了屬性和方法,屬性用來存儲描述類的數據,方法用來描述類的行為。在面向對象編程中,生活中的事物都可以模擬成程序中的對象,例如一個教務系統軟件中一定有教師相關信息,每一位教師都是一個教師對象,可以創建教師類來描述教師對象,示例代碼如下:

上面的代碼為教師類添加了3個屬性,name屬性用來描述教師的名字,number屬性用來描述教師的編號,subject屬性用來描述教師教學的課程。除了屬性外,還為教師對象添加了sayHi方法與teaching方法,方法的用法和函數一樣,只是在調用時需要用對象來調用,并且方法中會自動將當前對象綁定到this關鍵字上。也就是說,在方法中可以通過this關鍵字獲取對象的屬性信息,也可以調用其他方法。方法也需要通過點語法來進行調用,例如:

類中還有兩個非常特殊的方法:Setters方法與Getters方法。Setters方法用來設置對象屬性,Getters方法用來獲取對象屬性。其實當我們使用點語法訪問對象屬性信息時,調用的就是Setters方法或Getters方法,在定義屬性時,Dart會自動生成默認的Setters方法和Getters方法。Setters方法和Getters方法的另一大作用是定義附加屬性,附加屬性也可以理解為計算屬性,即這些數據通常不是描述對象的最原始數據,而是通過計算得來的,例如:

上面的代碼中,description就是附加屬性,其并沒有真正占用內存空間進行存儲,而是通過其他屬性計算而來的。

3.2.3 抽象類與抽象方法

抽象類是面向對象開發中較為難理解的一點。在Dart中,抽象類中可以定義抽象方法。所謂抽象方法,是指只有定義卻沒有實現的方法,抽象是面向接口開發的基礎。以生活中汽車產品的生產為例,一輛完整的汽車的生產往往需要多個廠家合作,例如發動機生產廠家、輪胎生產廠家、門窗內設生產廠家等。不同的廠家生產的配件若要完美地組合成一輛汽車,則必須遵守統一的標準,也可以理解為按照實現的協議進行生產。在編程中也是這樣的,一個復雜的程序可能需要很多開發者甚至多個部門進行配合開發,每個開發者或部門負責一個模塊,而模塊之間又可以進行交互與連通,這時在程序真正編寫前,我們就需要先約定協議、制定接口。

現在你應該理解了,抽象類實際上就是一個接口,接口中定義了未實現的方法告訴調用者:如果有類實現了這個接口,這個類就擁有接口所描述的功能。例如,我們可以為教師類定義一個接口,示例如下:

上面的TeacherInterface接口中只定義了一個抽象方法,Teacher類可以對這個接口進行實現,示例代碼如下:

一個類也可以同時實現多個接口,例如再定義一個人類接口,示例如下:

抽象類不可以被實例化,即不能直接使用抽象類來構造實例對象,只能通過實現這個抽象類接口的類或者繼承它的子類來實例化對象。關于繼承的內容,后面會介紹。

3.2.4 類的繼承

繼承是類的重要特性。子類繼承父類后,可以直接使用父類中定義的屬性和方法,并且子類可以對父類的方法進行重寫以實現定制化的功能。繼承其實很容易理解,現實中的事物為了方便描述與歸納,也會進行分門別類,例如生物界可以分為動物和植物,動物類下面又可以分出魚類、鳥類等,動物類就是生物的子類,魚類、鳥類又是動物類的子類。越是上層的類,封裝的屬性和方法越通用,子類會在父類的基礎上進行擴展,增加許多獨特的屬性和方法。

在Dart中,使用extends關鍵字進行類的繼承。以教師類為例,我們可以定義一個人類作為其父類,示例如下:

如上面的代碼所示,Teacher類直接繼承了People類的姓名、年齡屬性和sayHi方法。但是需要注意,構造方法是不會被繼承的,在Teacher類中可以使用super關鍵字來調用父類的方法,包括構造方法。子類也可以重載父類的方法,并且在重載時可以調用對應的父類方法,例如:

其中,@override關鍵字可以省略,這個關鍵字的作用僅僅是標注這個方法是子類重載父類的。

3.2.5 運算符重載

我們前面在學習運算符相關內容時了解到,Dart中的運算符非常靈活,例如加法運算符除了可以用在數值的加法運算外,在字符串對象間使用也可以實現字符串的拼接功能。Dart中的運算符之所以如此靈活,是由于Dart是一門完全面向對象的語言,而運算符的運算實質是方法的調用。因此,我們也可以為自定義的類添加運算符方法,例如:

上面的代碼定義了一個尺寸類Size,類中定義了寬度與高度兩個屬性,operator關鍵字用來進行運算符的重載,其格式如下:

上面的代碼重載了Size類的加法運算,當將兩個Size對象進行相加時,分別將它們的寬度和高度進行相加,并將新的對象返回。

重載運算符非常簡單,卻是非常強大的一個功能,在Dart中支持重載的運算符如表3-1所示。

表3-1 支持重載的運算符

3.2.6 noSuchMethod方法

對于非抽象類,當定義了一個沒有實現的方法時,代碼的運行會產生異常,例如:

運行上面的代碼,控制臺會拋出如下的異常信息:

其實,如果一個類實現了某個接口或者繼承了某個抽象類,卻沒有全部實現接口和抽象類中聲明的方法,就會產生如上的異常。然而實際開發中,接口或抽象類中的方法有時并不需要全部實現,這時可以選擇重載noSuchMethod方法,如果重載了這個方法,當對象調用到這些未實現的方法時,就會執行noSuchMethod方法,例如:

需要注意,雖然重載noSuchMethod方法可以避免調用未定義方法異常的產生,但是其也會掩蓋代碼邏輯中的錯誤,在實際開發時,要盡量少使用這種方式。

3.2.7 枚舉類型

枚舉是一種特殊的類型,其用來描述有限個數的數據集合。比如前面在定義教師類時,其中定義了一個科目的屬性,雖然我們將其定義為字符串類型,但是這并不十分嚴謹,教師所教學科目的類目是有限的,而且應該是固定的,不會隨意增減,對于這種情況,使用枚舉非常合適。

enum關鍵字用來定于枚舉,其實枚舉與數組類似,其中的數據也都有下標,從0開始,例如:

也可以使用values屬性來獲取枚舉中所有的值,例如:

更多時候,枚舉會和多分支語句結合使用,示例代碼如下:

3.2.8 擴展類的功能——Mixin特性

Mixin是Dart中非常強大的一個特性。通過前面的學習,我們知道,Dart只支持單繼承,即子類只能夠有一個父類。有的時候,我們需要集合多個類的功能來實現一個復雜的自定義類,就需要使用到Mixin特性。

Mixin從字面來理解為混合的意思,顧名思義,Mixin的主要作用也是進行混合,其允許一個類將其他類的功能混合進來,例如:

從控制臺的打印信息可以看出,Bird類已經成功混合了Descript類中的功能,可以調用其中定義的方法。

能夠進行混合的類被稱為Mixin類,Mixin類中不能實現構造方法,否則不能夠被其他類進行混合。使用with關鍵字來進行Mixin混合,Mixin支持多混合,例如:

還有一點需要額外注意,作為Mixin的類雖然不能夠定義構造方法,但是可以使用默認的構造方法進行實例化,如果不想使Mixin類實例化,那么可以使用mixin關鍵字代替class關鍵字來定義Mixin類,示例如下:

使用mixin定義的Mixin類不能夠被繼承,也不能夠進行實例化。Mixin類本身可以進行繼承,如果使用class關鍵字進行定義,就和普通類的集成語法一致,如果使用mixin關鍵字進行定義,就使用on關鍵字進行繼承,例如:

提示

mixin關鍵字在Dart 2.1版本之后可用。

學習了Mixin的基本概念與簡單用法,下面我們需要更深入地了解Mixin的工作原理,首先觀察下面的示例:

上面的代碼使用到了繼承和多混合,并且子類、父類、Mixin類中都對相同的方法進行了實現,運行代碼,控制臺會打印出“sub func”。可以看出,無論是Mixin還是繼承,子類中的方法實現優先級都是最高的,將子類實現的方法去掉,再次運行,控制臺將輸出“two func”,這說明Mixin中方法的優先級要高于父類中方法的優先級,并且在多混合中,后混合的優先級更高。

因此,在對于繼承和混合一起使用的復雜場景中,你需要牢記如下兩個原則:

(1)當前類中方法的優先級最高。

(2)Mixin中方法的優先級高于繼承父類方法的優先級,并且在混合時,Mixin從左到右優先級依次增高。

3.2.9 類屬性與類方法

前面我們定義類時定義的屬性和方法都是針對實例的,即由類的實例對象進行訪問或調用。其實,類本身也是一種對象,在類中也可以定義類屬性與類方法,使用類名直接進行訪問和調用。示例代碼如下:

類屬性也被稱為類靜態屬性,其通常用來存放某些固定的且在類的所有實例中共享的屬性,類方法也被稱為類靜態方法,通常會提供一些靜態的計算功能。

主站蜘蛛池模板: 盐源县| 承德市| 永靖县| 石楼县| 思茅市| 都昌县| 福泉市| 汝阳县| 永康市| 镇赉县| 慈溪市| 茶陵县| 同江市| 荆州市| 海兴县| 同德县| 昌江| 高密市| 丹凤县| 灵武市| 阿瓦提县| 平泉县| 马龙县| 晋州市| 郧西县| 舟山市| 大同市| 石柱| 徐闻县| 太康县| 吉木乃县| 常德市| 佛学| 新干县| 玉门市| 湖口县| 虎林市| 长葛市| 大新县| 云林县| 泗水县|