国产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 面向?qū)ο缶幊?/span> 區(qū)別 spring mvc java框架 排列 標準庫


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

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

本章我們介紹Kotlin的面向?qū)ο缶幊獭?/p>4.1 面向?qū)ο缶幊毯喪?p>50年代后期,在用FORTRAN語言編寫大型程序時,由于沒有封裝機制,那個時候的變量都是“全局變量”,那么就會不可避免的經(jīng)常出現(xiàn)變量名沖突問題。在ALGOL60中采用了以 Begin - End 為標識的程序塊,使塊內(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ù)言大師”。他是當今IT界屈指可數(shù)的技術(shù)天才級人物。</p>
<p>面向?qū)ο缶幊趟枷胫饕菑?fù)用性和靈活性(彈性)。復(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ù),成員變量)、行為(操作,成員方法)和標識(成員名,內(nèi)存地址)。</p>
<p>面向?qū)ο笳Z言其實是對現(xiàn)實生活中的實物的抽象。</p>
<p>每個對象能夠接受的請求(消息)由對象的接口所定義,而在程序中必須由滿足這些請求的代碼,這段代碼稱之為這個接口的實現(xiàn)。當向某個對象發(fā)送消息(請求)時,這個對象便知道該消息的目的(該方法的實現(xiàn)已定義),然后執(zhí)行相應(yīng)的代碼。</p>
<p>我們經(jīng)常說一些代碼片段是優(yōu)雅的或美觀的,實際上意味著它們更容易被人類有限的思維所處理。</p>
<p>對于程序的復(fù)合而言,好的代碼是它的表面積要比體積增長的慢。</p>
<p>代碼塊的“表面積”是是我們復(fù)合代碼塊時所需要的信息(接口API協(xié)議定義)。代碼塊的“體積”就是接口內(nèi)部的實現(xiàn)邏輯(API背后的實現(xiàn)代碼)。</p>
<p>在面向?qū)ο缶幊讨?,一個理想的對象應(yīng)該是只暴露它的抽象接口(純表面, 無體積),其方法則扮演箭頭的角色。如果為了理解一個對象如何與其他對象進行復(fù)合,當你發(fā)現(xiàn)不得不深入挖掘?qū)ο蟮膶崿F(xiàn)之時,此時你所用的編程范式的原本優(yōu)勢就蕩然無存了。</p>
<p>面向?qū)ο缶幊淌且环N編程思想,相比于早期的結(jié)構(gòu)化程序設(shè)計,抽象層次更高,思考解決問題的方式上也更加貼近人類的思維方式?,F(xiàn)代編程語言基本都支持面向?qū)ο缶幊谭妒健?/p>
<p>計算機領(lǐng)域中的所有問題,都可以通過向上一層進行抽象封裝來解決.這里的封裝的本質(zhì)概念,其實就是“映射”。從面向過程到面向?qū)ο?,再到設(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ū)ο缶幊袒陬惥幊?,更加貼近人類解決問題的習慣方法。讓軟件世界更像現(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運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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='$name', age=$age, sex='$sex')"    }}</code>

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

在代碼中這樣使用Person類

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

輸出

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<code class="javascript">person = Person(name='Jack', age=29, sex='M')</code>

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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='$name', age=$age, sex='$sex')"    }}</code>

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

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

輸出

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<code class="javascript">person1 = Person1(name='Jack', age=29, sex='M')</code>

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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)鍵字指向當前類對象實例        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='$name', age=$age, sex='$sex')"    }}</code>

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

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

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

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

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>右擊鼠標操作</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運行復(fù)制
<code class="javascript">constructor()</code>

選擇一個 name 屬性,生成

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

選擇name,age屬性生成

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

3個屬性都選擇,生成

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<code class="javascript">class Person(var name: String, var age: Int, var sex: String)</code>

而當確實需要通過比較復(fù)雜的邏輯來構(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運行復(fù)制
<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運行復(fù)制
<code class="javascript">val s = Shape() // 編譯不通過!不能實例化抽象類</code>

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

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

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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運行復(fù)制
<code class="javascript">abstract class Shape {    ...    open fun onClick() {        println("I am Clicked!")    }}</code>

在子類中這樣覆蓋重寫

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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運行復(fù)制
<code class="javascript">12.0Rectangle is Clicked!</code>

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

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運行復(fù)制
<code class="javascript">interface ProjectService</code>

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

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

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

第4章 類與面向?qū)ο缶幊痰?章 類與面向?qū)ο缶幊? /></figure><p>覆蓋沖突</p><p>這個時候,我們可以使用下面的語法來調(diào)用:</p>代碼語言:javascript<i class=代碼運行次數(shù):0運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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)下標的屬性值,按聲明順序排列copy() 函數(shù): 根據(jù)舊對象屬性重新 new LoginUser(username, password) 一個對象出來

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

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

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

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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 標準庫提供了 Pair 和 Triple數(shù)據(jù)類 。

4.6 注解

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

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

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

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

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

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

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

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

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

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

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

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

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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運行復(fù)制
<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>

我們可以看到,當訪問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運行復(fù)制
<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運行復(fù)制
<code class="javascript">@FunctionalInterfacepublic interface Runnable {    public abstract void run();}</code>

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

代碼語言:javascript代碼運行次數(shù):0運行復(fù)制
<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ū)ο缶幊谭妒降闹С稚?,Kotlin相比于Java增加不少有趣的功能與特性支持,這使得我們代碼寫起來更加方便快捷了。

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

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

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻,版權(quán)歸原作者所有,本站不承擔相應(yīng)法律責任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請聯(lián)系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)

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

普通投資者可通過追蹤“聰明錢”發(fā)現(xiàn)潛力代幣,其為高盈利地址,關(guān)注其動向能提供領(lǐng)先指標。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)入場

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è)施并爭取潛在空投機會。

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

比特幣、Chainlink與RWA的聯(lián)動上漲標志著加密市場正轉(zhuǎn)向機構(gòu)敘事主導。比特幣作為機構(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ū)動的新階段。

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ù)處理與訓練;Render Network提供分布式GPU算力,服務(wù)AIGC圖形渲染;Arweave以永久存儲特性支撐AI模型權(quán)重和數(shù)據(jù)溯源;三者結(jié)合技術(shù)升級與生態(tài)資本推動,正從邊緣走向AI底層核心。

加密市值突破三萬億美元:資金正押注哪些板塊? 加密市值突破三萬億美元:資金正押注哪些板塊? Jul 16, 2025 am 09:45 AM

加密市值突破三萬億美元,資金主要押注七大板塊。1.人工智能(AI) 區(qū)塊鏈:熱門幣種包括FET、RNDR、AGIX,幣安和OKX上線相關(guān)交易對及活動,資金押注AI與去中心化算力、數(shù)據(jù)整合;2.Layer2與模塊化區(qū)塊鏈:ARB、OP、ZK系、TIA受關(guān)注,HTX上線模塊化資產(chǎn)并提供返傭,資金看好其對DeFi、GameFi的支撐;3.RWA(真實世界資產(chǎn)):ONDO、POLYX、XDC等掛鉤現(xiàn)實資產(chǎn),OKX增設(shè)RWA專區(qū),資金預(yù)期傳統(tǒng)金融鏈上遷移;4.公鏈與平臺幣:SOL、BNB、HT、OKB強勢

狗狗幣、Pepe、Brett橫掃模因賽道:投機還是新敘事? 狗狗幣、Pepe、Brett橫掃模因賽道:投機還是新敘事? Jul 16, 2025 am 09:57 AM

狗狗幣、Pepe、Brett正引領(lǐng)模因幣熱潮,Dogecoin(DOGE)作為鼻祖穩(wěn)居市值榜首,Pepe(PEPE)憑借社群梗文化實現(xiàn)數(shù)百倍漲幅,Brett(BRETT)作為Base鏈新星以獨特視覺風格迅速走紅;三者分別于2013年、2023年、2024年發(fā)行,技術(shù)上Dogecoin基于Litecoin,Pepe與Brett為ERC-20代幣且后者依托Base鏈提升效率,社區(qū)方面DOGE推特粉絲超300萬,PepeReddit活躍度領(lǐng)先,Brett在Base鏈熱度攀升,上線平臺方面DOGE已登陸

山寨幣全線反彈:新一輪牛市已經(jīng)啟動?是否值得入場? 山寨幣全線反彈:新一輪牛市已經(jīng)啟動?是否值得入場? Jul 16, 2025 am 09:48 AM

是的,山寨幣反彈可能預(yù)示新一輪牛市醞釀階段開啟,但入場需謹慎。1. 市場情緒回暖,幣安、歐易、火幣等平臺山寨幣交易量激增,資金流入AI、Layer2、GameFi板塊;2. 山寨反彈呈現(xiàn)牛市初期特征,比特幣企穩(wěn)、熱點輪動加快、新項目頻繁上線;3. 是否入場需根據(jù)投資策略判斷:長期投資者可逐步建倉龍頭項目,短線交易者可關(guān)注活躍幣種波段機會,小倉位嘗試新幣需避免追高;4. 后續(xù)需觀察比特幣能否突破前高、三大平臺資金流向、美聯(lián)儲政策及鏈上活躍度等關(guān)鍵指標以判斷行情持續(xù)性。

如何刷新或重新安裝窗口而不會丟失數(shù)據(jù) 如何刷新或重新安裝窗口而不會丟失數(shù)據(jù) Jul 17, 2025 am 03:23 AM

重裝或刷新Windows系統(tǒng)時,數(shù)據(jù)丟失并非不可避免,關(guān)鍵在于正確區(qū)分刷新與重裝并做好準備。1.刷新(Refresh)保留個人文件、應(yīng)用和設(shè)置,適合系統(tǒng)運行緩慢或有小問題時使用;2.重裝(Reset)可選是否保留文件,若不勾選將刪除所有內(nèi)容,操作前務(wù)必確認選項;3.刷新操作可通過“設(shè)置→系統(tǒng)→恢復(fù)”中選擇“保留我的文件”,過程約半小時至一小時,文檔、圖片等文件不會丟失;4.若必須重裝系統(tǒng),方法一是在系統(tǒng)正常時使用“重置此電腦”并勾選保留文件;方法二是手動備份重要文件到外接設(shè)備或云盤,安裝時避免格

See all articles