国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁 系統(tǒng)教程 Windows系列 第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊?/span>

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊?/h1> May 07, 2025 pm 04:42 PM
windows 電腦 access 工具 ai 物件導(dǎo)向程式設(shè)計 差別 spring mvc java框架 排列 標(biāo)準(zhǔn)函式庫


第4章類與面向?qū)ο缶幊?/p>

在前面的章節(jié)中,我們學(xué)習(xí)了Kotlin的語言基礎(chǔ)知識、類型系統(tǒng)等相關(guān)的知識。在本章節(jié)以及下一章中,我們將一起來學(xué)習(xí)Kotlin對面向?qū)ο缶幊桃约昂瘮?shù)式編程的支持。

本章我們介紹Kotlin的面向?qū)ο缶幊獭?/p> 4.1 面向?qū)ο缶幊毯喪?p>50年代後期,在用FORTRAN語言編寫大型程序時,由於沒有封裝機制,那個時候的變量都是“全局變量”,那麼就會不可避免的經(jīng)常出現(xiàn)變量名衝突問題。在ALGOL60中採用了以Begin - End 為標(biāo)識的程序塊,使塊內(nèi)變量名是局部的,以避免它們與程序中塊外的同名變量相衝突。在編程語言中首次提供了封裝(保護)的機制。此後,程序塊結(jié)構(gòu)廣泛用於Pascal 、Ada、C等高級語言之中。

60年代中後期,Simula語言在ALGOL基礎(chǔ)上研製開發(fā),它將ALGOL的塊結(jié)構(gòu)概念向前發(fā)展一步,提出了對象的概念,並使用了類,也支持類繼承。其後的發(fā)展簡史如下圖所示:

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊?></figure><p>面向?qū)ο蟀l(fā)展簡史</p>
<p>阿倫·凱(Alan Kay)是Smalltalk面向?qū)ο缶幊陶Z言的發(fā)明人之一,也是面向?qū)ο缶幊趟枷氲膭?chuàng)始人之一,同時,他還是筆記本電腦最早的構(gòu)想者和現(xiàn)代Windows GUI的建築師。最早提出PC概念和互聯(lián)網(wǎng)的也是阿倫·凱,所以人們都尊稱他為“預(yù)言大師”。他是當(dāng)今IT界屈指可數(shù)的技術(shù)天才級人物。</p>
<p>面向?qū)ο缶幊趟枷胫饕茄}用性和靈活性(彈性)。復(fù)用性是面向?qū)ο缶幊痰囊粋€主要機制。靈活性主要是應(yīng)對變化的特性,因為客戶的需求是不斷改變的,怎樣適應(yīng)客戶需求的變化,這是軟件設(shè)計靈活性或者說是彈性的問題。</p>
<p> Java是一種面向?qū)ο缶幊陶Z言,它基於Smalltalk語言,作為OOP語言,它具有以下五個基本特性:</p>
<p> 1.萬物皆對象,每一個對像都會存儲數(shù)據(jù),並且可以對自身執(zhí)行操作。因此,每一個對象包含兩部分:成員變量和成員方法。在成員方法中可以改變成員變量的值。</p>
<p> 2.程序是對象的集合,他們通過發(fā)送消息來告知彼此所要做的事情,也就是調(diào)用相應(yīng)的成員函數(shù)。</p>
<p> 3.每一個對像都有自己的由其他對象所構(gòu)成的存儲,也就是說在創(chuàng)建新對象的時候可以在成員變量中使用已存在的對象。</p>
<p> 4.每個對像都擁有其類型,每個對像都是某個類的一個實例,每一個類區(qū)別於其它類的特性就是可以向它發(fā)送什麼類型的消息,也就是它定義了哪些成員函數(shù)。</p>
<p> 5.某一個特定類型的所有對像都可以接受同樣的消息。另一種對對象的描述為:對象具有狀態(tài)(數(shù)據(jù),成員變量)、行為(操作,成員方法)和標(biāo)識(成員名,內(nèi)存地址)。</p>
<p>面向?qū)ο笳Z言其實是對現(xiàn)實生活中的實物的抽象。</p>
<p>每個對象能夠接受的請求(消息)由對象的接口所定義,而在程序中必須由滿足這些請求的代碼,這段代碼稱之為這個接口的實現(xiàn)。當(dāng)向某個對象發(fā)送消息(請求)時,這個對象便知道該消息的目的(該方法的實現(xiàn)已定義),然後執(zhí)行相應(yīng)的代碼。</p>
<p>我們經(jīng)常說一些代碼片段是優(yōu)雅的或美觀的,實際上意味著它們更容易被人類有限的思維所處理。</p>
<p>對於程序的複合而言,好的代碼是它的表面積要比體積增長的慢。</p>
<p>代碼塊的“表面積”是是我們複合代碼塊時所需要的信息(接口API協(xié)議定義)。代碼塊的“體積”就是接口內(nèi)部的實現(xiàn)邏輯(API背後的實現(xiàn)代碼)。</p>
<p>在面向?qū)ο缶幊讨校粋€理想的對象應(yīng)該是只暴露它的抽象接口(純表面, 無體積),其方法則扮演箭頭的角色。如果為了理解一個對像如何與其他對象進行複合,當(dāng)你發(fā)現(xiàn)不得不深入挖掘?qū)ο蟮膶崿F(xiàn)之時,此時你所用的編程範(fàn)式的原本優(yōu)勢就蕩然無存了。</p>
<p>面向?qū)ο缶幊淌且环N編程思想,相比於早期的結(jié)構(gòu)化程序設(shè)計,抽象層次更高,思考解決問題的方式上也更加貼近人類的思維方式。現(xiàn)代編程語言基本都支持面向?qū)ο缶幊坦?fàn)式。</p>
<p>計算機領(lǐng)域中的所有問題,都可以通過向上一層進行抽象封裝來解決.這裡的封裝的本質(zhì)概念,其實就是“映射”。從面向過程到面向?qū)ο螅俚皆O(shè)計模式,架構(gòu)設(shè)計,面向服務(wù),Sass/Pass/Iass等等的思想,各種軟件理論思想五花八門,但萬變不離其宗——</p>你要解決一個怎樣的問題?你的問題領(lǐng)域是怎樣的?你的模型(數(shù)據(jù)結(jié)構(gòu))是什麼?你的算法是什麼?你對這個世界的本質(zhì)認知是怎樣的?你的業(yè)務(wù)領(lǐng)域的邏輯問題,流程是什麼? 等等。<p>面向?qū)ο缶幊痰囊袁F(xiàn)實世界中的事物(對象)為中心來思考, 認識問題, 並根據(jù)這些事物的本質(zhì)特徵, 把它們抽象表示為系統(tǒng)中的類。其核心思想可以用下圖簡要說明: </p>
<figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊?></figure><p>面向?qū)ο缶幊?/p>
<p>面向?qū)ο缶幊袒额惥幊?,更加貼近人類解決問題的習(xí)慣方法。讓軟件世界更像現(xiàn)實世界。面向?qū)ο缶幊掏ㄟ^抽像出關(guān)鍵的問題域來分解系統(tǒng)。對像不僅能表示具體的事物,還能表示抽象的規(guī)則、計劃或事件。關(guān)於面向?qū)ο缶幊痰暮诵牡母拍钊缦聢D所示</p>
<figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊?></figure><p>面向?qū)ο缶幊痰暮诵牡母拍?/p>4.2 聲明類<p>本節(jié)介紹Kotlin中類和構(gòu)造函數(shù)的聲明。</p> 4.2.1 空類<p>使用class關(guān)鍵字聲明類。我們可以聲明一個什麼都不干的類</p>代碼語言:javascript<i class=代碼運行次數(shù): 0 運行複製
<code class="javascript">class AnEmptyClassfun main(args: Array<String> ) { val anEmptyClass = AnEmptyClass() // Kotlin中不需要使用new println(anEmptyClass) println(anEmptyClass is AnEmptyClass) // 對象實例是AnEmptyClass類型println(anEmptyClass::class)}</code>

輸出

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">com.easy.kotlin.AnEmptyClass@2626b418trueclass com.easy.kotlin.AnEmptyClass (Kotlin reflection is not available)</code>
4.2.2 聲明類和構(gòu)造函數(shù)

在Kotlin中, 我們可以在聲明類的時候同時聲明構(gòu)造函數(shù),語法格式是在類的後面使用括號包含構(gòu)造函數(shù)的參數(shù)列表

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Person(var name: String, var age: Int, var sex: String) { // 聲明類和構(gòu)造函數(shù)override fun toString(): String { // override關(guān)鍵字,重寫toString() return "Person(name=&#39;$name&#39;, age=$age, sex=&#39;$sex&#39;)" }}</code>

使用這樣的簡潔語法,可以通過主構(gòu)造器來定義屬性並初始化屬性值(這裡的屬性值可以是var或val)。

在代碼中這樣使用Person類

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">val person = Person("Jack", 29, "M")println("person = ${person}")</code>

輸出

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">person = Person(name=&#39;Jack&#39;, age=29, sex=&#39;M&#39;)</code>

另外,我們也可以先聲明屬性,等到構(gòu)造實例對象的時候再去初始化屬性值,那麼我們的Person類可以聲明如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Person1 { lateinit var name: String // lateinit 關(guān)鍵字表示該屬性延遲初始化var age: Int = 0 // lateinit 關(guān)鍵字不能修飾primitive 類型lateinit var sex: String override fun toString(): String { return "Person1(name=&#39;$name&#39;, age=$age, sex=&#39;$sex&#39;)" }}</code>

我們可以在代碼中這樣創(chuàng)建Person1的實例對象

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> val person1 = Person1() person1.name = "Jack" person1.age = 29 person1.sex = "M" println("person1 = ${person1}")</code>

輸出

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">person1 = Person1(name=&#39;Jack&#39;, age=29, sex=&#39;M&#39;)</code>

如果我們想聲明一個具有多種構(gòu)造方式的類,可以使用constructor 關(guān)鍵字聲明構(gòu)造函數(shù),示例代碼如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Person2() { // 無參的主構(gòu)造函數(shù)lateinit var name: String var age: Int = 0 lateinit var sex: String constructor(name: String) : this() { // this 關(guān)鍵字指向當(dāng)前類對象實例this.name = name } constructor(name: String, age: Int) : this(name) { this.name = name this.age = age } constructor(name: String, age: Int, sex: String) : this(name, age) { this.name = name this.age = age this.sex = sex } override fun toString(): String { return "Person1(name=&#39;$name&#39;, age=$age, sex=&#39;$sex&#39;)" }}</code>

上面的寫法,總體來看也有些樣板代碼,其實在IDEA中,我們寫上面的代碼,只需要寫下面的3行,剩下的就交給IDEA自動生成了

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Person2 { lateinit var name: String var age: Int = 0 lateinit var sex: String}</code>

自動生成構(gòu)造函數(shù)的操作示意圖

1.在當(dāng)前類中“右擊”鼠標(biāo)操作,選擇Generate (在Mac上的快捷鍵是Command N)

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>右擊鼠標(biāo)操作</p>點擊之後,跳出對話框:生成次級構(gòu)造函數(shù)<figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>選擇Generate</p>選擇構(gòu)造函數(shù)的參數(shù)<figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>生成次級構(gòu)造函數(shù)</p><p>選中相應(yīng)的屬性,點擊OK,即可生成。</p><p>一個屬性都不選,生成</p>代碼語言:javascript<i class=代碼運行次數(shù): 0 運行複製
<code class="javascript">constructor()</code>

選擇一個name 屬性,生成

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> constructor(name: String) { this.name = name }</code>

選擇name,age屬性生成

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> constructor(name: String, age: Int) : this(name) { this.name = name this.age = age }</code>

3個屬性都選擇,生成

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> constructor(name: String, age: Int, sex: String) : this(name, age) { this.name = name this.age = age this.sex = sex }</code>

最後,我們可以在代碼中這樣創(chuàng)建Person2的實例對象

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> val person21 = Person2() person21.name = "Jack" person21.age = 29 person21.sex = "M" println("person21 = ${person21}") val person22 = Person2("Jack", 29) person22.sex = "M" println("person22 = ${person22}") val person23 = Person2("Jack", 29, "M") println("person23 = ${person23}")</code>

實際上,我們在編程實踐中用到最多的構(gòu)造函數(shù),還是這個

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Person(var name: String, var age: Int, var sex: String)</code>

而當(dāng)確實需要通過比較複雜的邏輯來構(gòu)建一個對象的時候,可採用構(gòu)建者(Builder)模式來實現(xiàn)。

4.3 抽像類與接口

抽像類表示“is-a”的關(guān)係,而接口所代表的是“has-a”的關(guān)係。

抽像類用來表徵問題領(lǐng)域的抽象概念。所有編程語言都提供抽像機制。機器語言是對機器的模仿抽象,彙編語言是對機器語言的高層次抽象,高級語言(Fortran,C,Basic等)是對彙編的高層次抽象。而我們這裡所說的面向?qū)ο缶幊陶Z言是對過程函數(shù)的高層次封裝。這個過程如下圖所示

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>編程語言的抽像機制</p><p>抽像類和接口是Kotlin語言中兩種不同的抽象概念,他們的存在對多態(tài)提供了非常好的支持。這個機制跟Java相同。</p> 4.3.1 抽像類與抽象成員<p>抽像是相對於具象而言。例如設(shè)計一個圖形編輯軟件,問題領(lǐng)域中存在著長方形(Rectangle)、圓形(Circle)、三角形(Triangle)等這樣一些具體概念,它們是具象。但是它們又都屬於形狀(Shape)這樣一個抽象的概念。它們的關(guān)係如下圖所示</p><figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>形狀Shape的抽象繼承關(guān)係</p><p>對應(yīng)的Kotlin代碼如下</p>代碼語言:javascript<i class=代碼運行次數(shù): 0 運行複製
<code class="javascript">package com.easy.kotlinabstract class Shapeclass Rectangle : Shape() // 繼承類的語法是使用冒號: , 父類需要在這里使用構(gòu)造函數(shù)初始化class Circle : Shape()class Triangle : Shape()</code>

因為抽象的概念在問題領(lǐng)域中沒有對應(yīng)的具體概念,所以抽像類是不能夠?qū)嵗?。下面的代碼編譯器會報錯

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">val s = Shape() // 編譯不通過!不能實例化抽象類</code>

我們只能實例化它的繼承子類。代碼示例如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">val r = Rectangle()println(r is Shape) // true</code>

現(xiàn)在我們有了抽像類,但是沒有成員。通常一個類的成員有屬性和函數(shù)。抽像類的成員也必須是抽象的,需要使用abstract 關(guān)鍵字修飾。下面我們聲明一個抽像類Shape,並帶有width ,heigth,radius屬性和area() 函數(shù), 代碼如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">abstract class Shape { abstract var width: Double abstract var heigth: Double abstract var radius: Double abstract fun area(): Double}</code>

這個時候,繼承抽像類Shape的方法如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Rectangle(override var width: Double, override var heigth: Double, override var radius: Double) : Shape() { // 聲明類的同時也聲明了構(gòu)造函數(shù)override fun area(): Double { return heigth * width }}class Circle(override var width: Double, override var heigth: Double, override var radius: Double) : Shape() { override fun area(): Double { return 3.14 * radius * radius }}</code>

其中,override 是覆蓋寫父類屬性和函數(shù)的關(guān)鍵字。

在代碼中這樣調(diào)用具體實現(xiàn)的類的函數(shù)

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">fun main(args: Array<String> ) { val r = Rectangle(3.0, 4.0, 0.0) println(r.area()) // 12.0 val c = Circle(0.0, 0.0, 4.0) println(c.area()) // 50.24}</code>

抽像類中可以有帶實現(xiàn)的函數(shù),例如我們在抽像類Shape中添加一個函數(shù)onClick()

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">abstract class Shape { ... fun onClick() { // 默認是final的,不可被覆蓋重寫println("I am Clicked!") }}</code>

那麼,我們在所有的子類中都可以直接調(diào)用這個onClick()函數(shù)

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> val r = Rectangle(3.0, 4.0, 0.0) r.onClick() // I am Clicked! val c = Circle(0.0, 0.0, 4.0) c.onClick() // I am Clicked!</code>

父類Shape中的onClick()函數(shù)默認是final的,不可被覆蓋重寫。如果想要開放給子類重新實現(xiàn)這個函數(shù),我們可以在前面加上open 關(guān)鍵字

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">abstract class Shape { ... open fun onClick() { println("I am Clicked!") }}</code>

在子類中這樣覆蓋重寫

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class Rectangle(override var width: Double, override var heigth: Double, override var radius: Double) : Shape() { override fun area(): Double { return heigth * width } override fun onClick(){ println("${this::class.simpleName} is Clicked!") }}fun main(args: Array<String> ) { val r = Rectangle(3.0, 4.0, 0.0) println(r.area()) r.onClick()}</code>

其中,this::class.simpleName 是Kotlin中的反射的API,在Gradle工程的build.gradle中需要添加依賴compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" ,我們將在後面的章節(jié)中詳細介紹。

上面的代碼運行輸出

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">12.0Rectangle is Clicked!</code>

當(dāng)子類繼承了某個類之後,便可以使用父類中的成員變量,但是並不是完全繼承父類的所有成員變量。具體的原則如下:

1.能夠繼承父類的public和protected成員變量;不能夠繼承父類的private成員變量;

2.對於父類的包訪問權(quán)限成員變量,如果子類和父類在同一個包下,則子類能夠繼承;否則,子類不能夠繼承;

3.對於子類可以繼承的父類成員變量,如果在子類中出現(xiàn)了同名稱的成員變量,則會發(fā)生隱藏現(xiàn)象,即子類的成員變量會屏蔽掉父類的同名成員變量。如果要在子類中訪問父類中同名成員變量,需要使用super關(guān)鍵字來進行引用。

4.3.2 接口

接口是一種比抽像類更加抽象的“類”。接口本身代表的是一種“類型”的概念。但在語法層面,接口本身不是類,不能實例化接口,我們只能實例化它的實現(xiàn)類。

接口是用來建立類與類之間的協(xié)議。實現(xiàn)該接口的實現(xiàn)類必須要實現(xiàn)該接口的所有方法。在Java 8 和Kotlin中,接口可以實現(xiàn)一些通用的方法。

接口是抽像類的延伸,Kotlin跟Java一樣,不支持同時繼承多個父類,也就是說繼承只能存在一個父類(單繼承)。但是接口不同,一個類可以同時實現(xiàn)多個接口(多組合),不管這些接口之間有沒有關(guān)係。這樣可以實現(xiàn)多重繼承。

和Java類似,Kotlin使用interface作為接口的關(guān)鍵詞:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">interface ProjectService</code>

Kotlin 的接口與Java 8 的接口類似。與抽像類相比,他們都可以包含抽象的方法以及方法的實現(xiàn):

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">interface ProjectService { val name: String val owner: String fun save(project: Project) fun print() { println("I am project") }}</code>

接口是沒有構(gòu)造函數(shù)的。我們使用冒號:語法來實現(xiàn)一個接口,如果有多個用,逗號隔開:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class ProjectServiceImpl : ProjectService // 跟繼承抽象類語法一樣,使用冒號class ProjectMilestoneServiceImpl : ProjectService, MilestoneService // 實現(xiàn)多個接口使用逗號( ,) 隔開</code>

在重寫print()函數(shù)時,因為我們實現(xiàn)的ProjectService、MilestoneService都有一個print()函數(shù),當(dāng)我們直接使用super.print()時,編譯器是無法知道我們想要調(diào)用的是那個里面的print函數(shù)的,這個我們叫做覆蓋衝突,如下圖所示

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>覆蓋衝突</p><p>這個時候,我們可以使用下面的語法來調(diào)用:</p>代碼語言:javascript<i class=代碼運行次數(shù): 0 運行複製
<code class="javascript">super<ProjectService> .print()super<MilestoneService> .print()</code>
4.4 object對象

單例模式很常用。它是一種常用的軟件設(shè)計模式。例如,Spring中的Bean默認就是單例。通過單例模式可以保證系統(tǒng)中一個類只有一個實例。即一個類只有一個對象實例。

Kotlin中沒有靜態(tài)屬性和方法,但是可以使用關(guān)鍵字object聲明一個object 單例對象:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">package com.easy.kotlinobject User { val username: String = "admin" val password: String = "admin" fun hello() { println("Hello, object !") }}fun main(args: Array<String> ) { println(User.username) // 跟Java的靜態(tài)類一樣的調(diào)用形式println(User.password) User.hello()}</code>

Kotlin中還提供了companion object關(guān)鍵字聲明:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class DataProcessor { companion object DataProcessor { fun process() { println("I am processing data ...") } }}fun main(args: Array<String> ) { DataProcessor.process() // I am processing data ...}</code>

一個類只能有1個伴生對象。

4.5 數(shù)據(jù)類

顧名思義,數(shù)據(jù)類就是只存儲數(shù)據(jù),不包含操作行為的類。 Kotlin的數(shù)據(jù)類可以為我們節(jié)省大量樣板代碼(Java 中強制我們要去寫一堆getter、setter,而實際上這些方法都是“不言自明”的),這樣最終代碼更易於理解和便於維護。

使用關(guān)鍵字為data class 創(chuàng)建一個只包含數(shù)據(jù)的類:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">data class LoginUser(val username: String, val password: String)</code>

在IDEA中提供了方便的Kotlin工具箱,我們可以把上面的代碼反編譯成等價的Java代碼。步驟如下

1.菜單欄選擇:Tools -> Kotlin -> Show Kotlin Bytecode

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>菜單欄選擇:Tools -> Kotlin -> Show Kotlin Bytecode</p>點擊Decompile <figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>點擊Decompile</p>反編譯之後的Java代碼<figure class=第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>反編譯之後的Java代碼</p><p>上面這段反編譯之後的完整的Java代碼是</p>代碼語言:javascript<i class=代碼運行次數(shù): 0 運行複製
<code class="javascript">public final class LoginUser { @NotNull private final String username; @NotNull private final String password; @NotNull public final String getUsername() { return this.username; } @NotNull public final String getPassword() { return this.password; } public LoginUser(@NotNull String username, @NotNull String password) { Intrinsics.checkParameterIsNotNull(username, "username"); Intrinsics.checkParameterIsNotNull(password, "password"); super(); this.username = username; this.password = password; } @NotNull public final String component1() { return this.username; } @NotNull public final String component2() { return this.password; } @NotNull public final LoginUser copy(@NotNull String username, @NotNull String password) { Intrinsics.checkParameterIsNotNull(username, "username"); Intrinsics.checkParameterIsNotNull(password, "password"); return new LoginUser(username, password); } // $FF: synthetic method // $FF: bridge method @NotNull public static LoginUser copy$default(LoginUser var0, String var1, String var2, int var3, Object var4) { if ((var3 & 1) != 0) { var1 = var0.username; } if ((var3 & 2) != 0) { var2 = var0.password; } return var0.copy(var1, var2); } public String toString() { return "LoginUser(username=" this.username ", password=" this.password ")"; } public int hashCode() { return (this.username != null ? this.username.hashCode() : 0) * 31 (this.password != null ? this.password.hashCode() : 0); } public boolean equals(Object var1) { if (this != var1) { if (var1 instanceof LoginUser) { LoginUser var2 = (LoginUser)var1; if (Intrinsics.areEqual(this.username, var2.username) && Intrinsics.areEqual(this.password, var2.password)) { return true; } } return false; } else { return true; } }}</code>

編譯器會從主構(gòu)造函數(shù)中聲明的屬性,自動創(chuàng)建以下函數(shù):

equals() / hashCode() 函數(shù)toString() 格式為"LoginUser(username=" this.username ", password=" this.password ")"component1(),component2() 函數(shù)返回對應(yīng)下標(biāo)的屬性值,按聲明順序排列copy() 函數(shù): 根據(jù)舊對象屬性重新new LoginUser(username, password) 一個對像出來

如果這些函數(shù)在類中已經(jīng)被明確定義了,或者從超類中繼承而來,編譯器就不再生成。

數(shù)據(jù)類有如下限制:

主構(gòu)造函數(shù)至少包含一個參數(shù)參數(shù)必須標(biāo)識為val 或者var不能為abstract, open, sealed 或者inner不能繼承其它類(但可以實現(xiàn)接口)

另外,數(shù)據(jù)類可以在解構(gòu)聲明中使用:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">package com.easy.kotlindata class LoginUser(val username: String, val password: String)fun main(args: Array<String> ) { val loginUser = LoginUser("admin", "admin") val (username, password) = loginUser println("username = ${username}, password = ${password}") // username = admin, password = admin}</code>

Kotlin 標(biāo)準(zhǔn)庫提供了Pair 和Triple數(shù)據(jù)類。

4.6 註解

註解是將元數(shù)據(jù)附加到代碼中。元數(shù)據(jù)信息由註解kotlin.Metadata定義。

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.CLASS)internal annotation class Metadata</code>

這個@Metadata信息存在於由Kotlin 編譯器生成的所有類文件中, 並由編譯器和反射讀取。例如,我們使用Kotlin聲明一個註解

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">annotation class Suspendable // Java中使用的是@interface Suspendable</code>

那麼,編譯器會生成對應(yīng)的元數(shù)據(jù)信息

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">@Retention(RetentionPolicy.RUNTIME)@Metadata( mv = {1, 1, 7}, bv = {1, 0, 2}, k = 1, d1 = {"\u0000\n\n\u0002\u0018\u0002\n\u0002\u0010\u001b\n\u0000\b\u0086\u0002\u0018\u00002\u00020\u0001B\u0000¨\u0006\u0002"}, d2 = {"Lcom/easy/kotlin/Suspendable;", "", "production sources for module kotlin_tutorials_main"})public @interface Suspendable {}</code>

Kotlin 的註解完全兼容Java 的註解。例如,我們在Kotlin中使用Spring Data Jpa

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">interface ImageRepository : PagingAndSortingRepository<Image, Long> { @Query("SELECT a from #{#entityName} a where a.isDeleted=0 and a.isFavorite=1 and a.category like %:searchText% order by a.gmtModified desc") fun searchFavorite(@Param("searchText") searchText: String, pageable: Pageable): Page<Image> @Throws(Exception::class) @Modifying @Transactional @Query("update #{#entityName} a set a.isFavorite=1,a.gmtModified=now() where a.id=?1") fun addFavorite(id: Long)}</code>

用起來跟Java的註解基本一樣。再舉個Kotlin使用Spring MVC註解的代碼實例

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">@Controllerclass MeituController { @Autowired lateinit var imageRepository: ImageRepository @RequestMapping(value = *arrayOf("/", "meituView"), method = arrayOf(RequestMethod.GET)) fun meituView(model: Model, request: HttpServletRequest): ModelAndView { model.addAttribute("requestURI", request.requestURI) return ModelAndView("meituView") }}</code>

從上面的例子,我們可以看出Kotlin使用Java框架非常簡單方便。

4.7 枚舉

Kotlin中使用enum class 關(guān)鍵字來聲明一個枚舉類。例如

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">enum class Direction { NORTH, SOUTH, WEST, EAST // 每個枚舉常量都是一個對象, 用逗號分隔}</code>

相比於字符串常量,使用枚舉能夠?qū)崿F(xiàn)類型安全。枚舉類有兩個內(nèi)置的屬性:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> public final val name: String public final val ordinal: Int</code>

分別表示的是枚舉對象的值跟下標(biāo)位置。例如上面的Direction枚舉類,它的枚舉對象的信息如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">>>> val north = Direction.NORTH>>> north.nameNORTH>>> north.ordinal0>>> north is Directiontrue</code>

每一個枚舉都是枚舉類的實例,它們可以被初始化:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF)}</code>

枚舉Color的枚舉對象的信息如下

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">>>> val c = Color.GREEN>>> cGREEN>>> c.rgb65280>>> c.ordinal1>>> c.nameGREEN</code>
4.8 內(nèi)部類4.8.1 普通嵌套類

Kotlin中,類可以嵌套。一個類可以嵌套在其他類中,而且可以嵌套多層。

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class NestedClassesDemo { class Outer { private val zero: Int = 0 val one: Int = 1 class Nested { fun getTwo() = 2 class Nested1 { val three = 3 fun getFour() = 4 } } }}</code>

測試代碼:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> val one = NestedClassesDemo.Outer().one val two = NestedClassesDemo.Outer.Nested().getTwo() val three = NestedClassesDemo.Outer.Nested.Nested1().three val four = NestedClassesDemo.Outer.Nested.Nested1().getFour()</code>

我們可以看出,代碼中NestedClassesDemo.Outer.Nested().getTwo() 訪問嵌套類的方式是直接使用類名.來訪問, 有多少層嵌套,就用多少層類名來訪問。

普通的嵌套類,沒有持有外部類的引用,所以是無法訪問外部類的變量的:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class NestedClassesDemo {class Outer { private val zero: Int = 0 val one: Int = 1 class Nested { fun getTwo() = 2 fun accessOuter() = { println(zero) // error, cannot access outer class println(one) // error, cannot access outer class } }}}</code>
4.8.2 嵌套內(nèi)部類

如果一個類Inner想要訪問外部類Outer的成員,可以在這個類前面添加修飾符inner。內(nèi)部類會帶有一個對外部類的對象的引用:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">package com.easy.kotlinclass NestedClassesDemo { class Outer { private val zero: Int = 0 val one: Int = 1 inner class Inner { fun accessOuter() = { println(zero) // works println(one) // works } } }}fun main(args: Array<String> ) { val innerClass = NestedClassesDemo.Outer().Inner().accessOuter()}</code>

我們可以看到,當(dāng)訪問inner class Inner的時候,我們使用的是Outer().Inner() , 這是持有了Outer的對象引用。跟普通嵌套類直接使用類名訪問的方式區(qū)分。

4.8.3 匿名內(nèi)部類

匿名內(nèi)部類,就是沒有名字的內(nèi)部類。既然是內(nèi)部類,那麼它自然也是可以訪問外部類的變量的。

我們使用對象表達式創(chuàng)建一個匿名內(nèi)部類實例:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">class NestedClassesDemo { class AnonymousInnerClassDemo { var isRunning = false fun doRun() { Thread(object : Runnable { // 匿名內(nèi)部類override fun run() { isRunning = true println("doRun : i am running, isRunning = $isRunning") } }).start() } }}</code>

如果對像是函數(shù)式Java 接口,即具有單個抽象方法的Java 接口的實例,例如上面的例子中的Runnable接口:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript">@FunctionalInterfacepublic interface Runnable { public abstract void run();}</code>

我們可以使用lambda表達式創(chuàng)建它,下面的幾種寫法都是可以的:

代碼語言:javascript代碼運行次數(shù): 0 運行複製
<code class="javascript"> fun doStop() { var isRunning = true Thread({ isRunning = false println("doStop: i am not running, isRunning = $isRunning") }).start() } fun doWait() { var isRunning = true val wait = Runnable { isRunning = false println("doWait: i am waiting, isRunning = $isRunning") } Thread(wait).start() } fun doNotify() { var isRunning = true val wait = { isRunning = false println("doNotify: i notify, isRunning = $isRunning") } Thread(wait).start() }</code>

更多關(guān)於Lambda表達式以及函數(shù)式編程相關(guān)內(nèi)容,我們將在下一章節(jié)中介紹。

本章小結(jié)

本章我們介紹了Kotlin面向?qū)ο缶幊痰奶匦裕?類與構(gòu)造函數(shù)、抽像類與接口、繼承與組合等知識,同時介紹了Kotlin中的註解類、枚舉類、數(shù)據(jù)類、嵌套類、內(nèi)部類、匿名內(nèi)部類、單例object對像等特性類。

總的來說,在面向?qū)ο缶幊坦?fàn)式的支持上,Kotlin相比於Java增加不少有趣的功能與特性支持,這使得我們代碼寫起來更加方便快捷了。

我們知道,在Java 8 中,引進了對函數(shù)式編程的支持:Lambda表達式、Function接口、stream API等,而在Kotlin中,對函數(shù)式編程的支持更加全面豐富,代碼寫起來也更加簡潔優(yōu)雅。下一章中,我們來一起學(xué)習(xí)Kotlin的函數(shù)式編程。

以上是第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊痰脑敿殐?nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

LayerZero、StarkNet、ZK生態(tài)預(yù)熱:空投紅利還能持續(xù)多久? LayerZero、StarkNet、ZK生態(tài)預(yù)熱:空投紅利還能持續(xù)多久? Jul 16, 2025 am 10:06 AM

空投紅利的持續(xù)時間不確定,但LayerZero、StarkNet和ZK生態(tài)仍具長期價值。 1. LayerZero通過輕量級協(xié)議實現(xiàn)跨鏈互操作性;2. StarkNet基於ZK-STARKs技術(shù)提供高效低成本的以太坊L2擴展方案;3. ZK生態(tài)(如zkSync、Scroll等)拓展零知識證明在擴容與隱私保護的應(yīng)用;4. 參與方式包括使用橋接工具、交互DApps、參與測試網(wǎng)、質(zhì)押資產(chǎn)等,旨在提前體驗下一代區(qū)塊鏈基礎(chǔ)設(shè)施並爭取潛在空投機會。

鏈上資金流向大曝光:聰明錢正在下注哪些新代幣? 鏈上資金流向大曝光:聰明錢正在下注哪些新代幣? Jul 16, 2025 am 10:15 AM

普通投資者可通過追蹤“聰明錢”發(fā)現(xiàn)潛力代幣,其為高盈利地址,關(guān)注其動向能提供領(lǐng)先指標(biāo)。 1.使用Nansen、Arkham Intelligence等工具分析鏈上數(shù)據(jù),查看聰明錢的買入與持倉情況;2.通過Dune Analytics獲取社區(qū)創(chuàng)建的儀表板,監(jiān)測資金流向;3.關(guān)注Lookonchain等平臺獲取實時情報。近期聰明錢正佈局再質(zhì)押與LRT賽道、DePIN項目、模塊化生態(tài)及RWA協(xié)議,如某LRT協(xié)議獲大量早期存款,某DePIN項目被持續(xù)積累,某遊戲公鏈獲產(chǎn)業(yè)金庫支持,某RWA協(xié)議吸引機構(gòu)入場

穩(wěn)定幣DAI適合什麼人使用_去中心化穩(wěn)定幣使用場景解析 穩(wěn)定幣DAI適合什麼人使用_去中心化穩(wěn)定幣使用場景解析 Jul 15, 2025 pm 11:27 PM

DAI適合重視去中心化理念、積極參與DeFi生態(tài)、需要跨鏈資產(chǎn)流動性及追求資產(chǎn)透明與自治的用戶。 1. 去中心化理念支持者信任智能合約與社區(qū)治理;2. DeFi用戶可用於借貸、質(zhì)押、流動性挖礦;3. 跨鏈用戶可實現(xiàn)多鏈資產(chǎn)靈活轉(zhuǎn)移;4. 治理參與者可通過投票影響系統(tǒng)決策。其主要場景包括去中心化借貸、資產(chǎn)避險、流動性挖礦、跨境支付及社區(qū)治理,同時需注意系統(tǒng)風(fēng)險、抵押波動風(fēng)險和技術(shù)門檻問題。

穩(wěn)定幣DAI和USDC哪個好_DAI適合長期持有嗎 穩(wěn)定幣DAI和USDC哪個好_DAI適合長期持有嗎 Jul 15, 2025 pm 11:18 PM

DAI適合長期持有嗎?答案取決於個人需求與風(fēng)險偏好。 1.DAI是去中心化穩(wěn)定幣,由加密資產(chǎn)超額抵押生成,適合追求抗審查和透明度的用戶;2.其穩(wěn)定性略遜於USDC,可能因抵押品波動出現(xiàn)輕微脫錨;3.適用於DeFi生態(tài)中的借貸、質(zhì)押及治理場景;4.需關(guān)注MakerDAO系統(tǒng)升級與治理風(fēng)險。若追求高穩(wěn)定性與合規(guī)保障,建議選擇USDC;若重視去中心化理念並積極參與DeFi應(yīng)用,則DAI具備長期持有價值。兩者結(jié)合使用亦可提升資產(chǎn)配置的安全性與靈活性。

以太坊智能合約的作用 以太坊智能合約的作用 Jul 15, 2025 pm 09:18 PM

以太坊智能合約的作用是實現(xiàn)去中心化、自動化和透明的協(xié)議執(zhí)行,其核心功能包括:1. 作為DApp的核心邏輯層,支持代幣發(fā)行、DeFi、NFT等功能;2. 通過代碼自動執(zhí)行合約,減少人為乾預(yù)與欺詐風(fēng)險;3. 構(gòu)建DeFi生態(tài),使用戶可直接進行借貸、交易等金融操作;4. 創(chuàng)建與管理數(shù)字資產(chǎn),確保唯一性與可驗證性;5. 提升供應(yīng)鏈與身份驗證的透明度與安全性;6. 支持DAO治理,實現(xiàn)去中心化決策。

比特幣、Chainlink、RWA共振上漲:加密市場進入機構(gòu)邏輯? 比特幣、Chainlink、RWA共振上漲:加密市場進入機構(gòu)邏輯? Jul 16, 2025 am 10:03 AM

比特幣、Chainlink與RWA的聯(lián)動上漲標(biāo)誌著加密市場正轉(zhuǎn)向機構(gòu)敘事主導(dǎo)。比特幣作為機構(gòu)配置的宏觀對沖資產(chǎn),為市場提供穩(wěn)定基礎(chǔ);Chainlink通過預(yù)言機和跨鏈技術(shù)成為連接現(xiàn)實與數(shù)字世界的關(guān)鍵橋樑;RWA則為傳統(tǒng)資本入場提供合規(guī)路徑。三者共同構(gòu)建了機構(gòu)入場的完整邏輯閉環(huán):1)配置BTC穩(wěn)定資產(chǎn)負債表;2)通過RWA拓展鏈上資產(chǎn)管理;3)依賴Chainlink建設(shè)底層基礎(chǔ)設(shè)施,預(yù)示市場進入由真實需求驅(qū)動的新階段。

一個穩(wěn)定幣多少美元 一個穩(wěn)定幣多少美元 Jul 15, 2025 pm 09:57 PM

穩(wěn)定幣的價值通常與美元1:1掛鉤,但受市場供需、投資者信心及儲備資產(chǎn)狀況等因素影響會出現(xiàn)小幅波動。例如USDT曾在2018年跌至0.87美元,USDC在2023年也因矽谷銀行危機短暫跌至0.87美元左右。穩(wěn)定幣的錨定機制主要包括:1.法幣儲備型(如USDT、USDC),依賴發(fā)行方儲備金;2.加密貨幣抵押型(如DAI),通過超額抵押其他加密貨幣維持穩(wěn)定;3.算法穩(wěn)定幣(如UST),依靠算法調(diào)整供應(yīng)量,但風(fēng)險較高。常見交易平臺推薦包括:1.Binance,提供豐富交易產(chǎn)品和強大流動性;2.OKX,以

Filecoin、Render、AI存儲預(yù)售升溫:Web3基礎(chǔ)設(shè)施爆點將至? Filecoin、Render、AI存儲預(yù)售升溫:Web3基礎(chǔ)設(shè)施爆點將至? Jul 16, 2025 am 09:51 AM

是的,Web3基礎(chǔ)設(shè)施正因AI需求升溫而迎來爆發(fā)預(yù)期。 Filecoin通過“Compute over Data”計劃整合計算能力,支持AI數(shù)據(jù)處理與訓(xùn)練;Render Network提供分佈式GPU算力,服務(wù)AIGC圖形渲染;Arweave以永久存儲特性支撐AI模型權(quán)重和數(shù)據(jù)溯源;三者結(jié)合技術(shù)升級與生態(tài)資本推動,正從邊緣走向AI底層核心。

See all articles