色偷偷91综合久久噜噜-色偷偷成人-色偷偷尼玛图亚洲综合-色偷偷人人澡久久天天-国内精品视频一区-国内精品视频一区二区三区

Hello! 歡迎來到小浪云!


第4章 類與面向對象編程第4章 類與面向對象編程


第4章 類與面向對象編程

在前面的章節中,我們學習了kotlin的語言基礎知識、類型系統等相關的知識。在本章節以及下一章中,我們將一起來學習kotlin面向對象編程以及函數式編程的支持。

本章我們介紹kotlin的面向對象編程。

4.1 面向對象編程簡史

50年代后期,在用FORTRAN語言編寫大型程序時,由于沒有封裝機制,那個時候的變量都是“全局變量”,那么就會不可避免的經常出現變量名沖突問題。在ALGOL60中采用了以 Begin – End 為標識的程序塊,使塊內變量名是局部的,以避免它們與程序中塊外的同名變量相沖突。在編程語言中首次提供了封裝(保護)的機制。此后,程序塊結構廣泛用于Pascal 、Ada、C等高級語言之中。

60年代中后期,Simula語言在ALGOL基礎上研制開發,它將ALGOL的塊結構概念向前發展一步,提出了對象的概念,并使用了類,也支持類繼承。其后的發展簡史如下圖所示:

第4章 類與面向對象編程第4章 類與面向對象編程

面向對象發展簡史

阿倫·凱(Alan Kay)是Smalltalk面向對象編程語言的發明人之一,也是面向對象編程思想的創始人之一,同時,他還是筆記本電腦最早的構想者和現代Windows GUI的建筑師。最早提出PC概念和互聯網的也是阿倫·凱,所以人們都尊稱他為“預言大師”。他是當今IT界屈指可數的技術天才級人物。

面向對象編程思想主要是復用性和靈活性(彈性)。復用性是面向對象編程的一個主要機制。靈活性主要是應對變化的特性,因為客戶的需求是不斷改變的,怎樣適應客戶需求的變化,這是軟件設計靈活性或者說是彈性的問題。

Java是一種面向對象編程語言,它基于Smalltalk語言,作為OOP語言,它具有以下五個基本特性:

1.萬物皆對象,每一個對象都會存儲數據,并且可以對自身執行操作。因此,每一個對象包含兩部分:成員變量和成員方法。在成員方法中可以改變成員變量的值。

2.程序是對象的集合,他們通過發送消息來告知彼此所要做的事情,也就是調用相應的成員函數

3.每一個對象都有自己的由其他對象所構成的存儲,也就是說在創建新對象的時候可以在成員變量中使用已存在的對象。

4.每個對象都擁有其類型,每個對象都是某個類的一個實例,每一個類區別于其它類的特性就是可以向它發送什么類型的消息,也就是它定義了哪些成員函數

5.某一個特定類型的所有對象都可以接受同樣的消息。另一種對對象的描述為:對象具有狀態(數據,成員變量)、行為(操作,成員方法)和標識(成員名,內存地址)。

面向對象語言其實是對現實生活中的實物的抽象。

每個對象能夠接受的請求(消息)由對象的接口所定義,而在程序中必須由滿足這些請求的代碼,這段代碼稱之為這個接口的實現。當向某個對象發送消息(請求)時,這個對象便知道該消息的目的(該方法的實現已定義),然后執行相應的代碼。

我們經常說一些代碼片段是優雅的或美觀的,實際上意味著它們更容易被人類有限的思維所處理。

對于程序的復合而言,好的代碼是它的表面積要比體積增長的慢。

代碼塊的“表面積”是是我們復合代碼塊時所需要的信息(接口API協議定義)。代碼塊的“體積”就是接口內部的實現邏輯(API背后的實現代碼)。

在面向對象編程中,一個理想的對象應該是只暴露它的抽象接口(純表面, 無體積),其方法則扮演箭頭的角色。如果為了理解一個對象如何與其他對象進行復合,當你發現不得不深入挖掘對象的實現之時,此時你所用的編程范式的原本優勢就蕩然無存了。

面向對象編程是一種編程思想,相比于早期的結構化程序設計,抽象層次更高,思考解決問題的方式上也更加貼近人類的思維方式。現代編程語言基本都支持面向對象編程范式。

計算機領域中的所有問題,都可以通過向上一層進行抽象封裝來解決.這里的封裝的本質概念,其實就是“映射”。從面向過程到面向對象,再到設計模式,架構設計,面向服務,sass/Pass/Iass等等的思想,各種軟件理論思想五花八門,但萬變不離其宗——

你要解決一個怎樣的問題?你的問題領域是怎樣的?你的模型(數據結構)是什么?你的算法是什么?你對這個世界的本質認知是怎樣的?你的業務領域的邏輯問題,流程是什么? 等等。

面向對象編程的以現實世界中的事物(對象)為中心來思考, 認識問題, 并根據這些事物的本質特征, 把它們抽象表示為系統中的類。其核心思想可以用下圖簡要說明:

第4章 類與面向對象編程第4章 類與面向對象編程

面向對象編程

面向對象編程基于類編程,更加貼近人類解決問題的習慣方法。讓軟件世界更像現實世界。面向對象編程通過抽象出關鍵的問題域來分解系統。對象不僅能表示具體的事物,還能表示抽象的規則、計劃或事件。關于面向對象編程的核心的概念如下圖所示

第4章 類與面向對象編程第4章 類與面向對象編程

面向對象編程的核心的概念

4.2 聲明類

本節介紹Kotlin中類和構造函數的聲明。

4.2.1 空類

使用class關鍵字聲明類。我們可以聲明一個什么都不干的類

代碼語言:Javascript代碼運行次數:0運行復制

class AnEmptyClassfun main(args: Array<string>) {    val anEmptyClass = AnEmptyClass() // Kotlin中不需要使用new    println(anEmptyClass)    println(anEmptyClass is AnEmptyClass) // 對象實例是AnEmptyClass類型    println(anEmptyClass::class)}</string>

輸出

代碼語言:JavaScript代碼運行次數:0運行復制

com.easy.kotlin.AnEmptyClass@2626b418trueclass com.easy.kotlin.AnEmptyClass (Kotlin reflection is not available)

4.2.2 聲明類和構造函數

在Kotlin中, 我們可以在聲明類的時候同時聲明構造函數,語法格式是在類的后面使用括號包含構造函數的參數列表

代碼語言:javascript代碼運行次數:0運行復制

class Person(var name: String, var age: Int, var sex: String) { // 聲明類和構造函數    override fun toString(): String { // override關鍵字,重寫toString()        return "Person(name='$name', age=$age, sex='$sex')"    }}

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

在代碼中這樣使用Person類

代碼語言:javascript代碼運行次數:0運行復制

val person = Person("Jack", 29, "M")println("person = ${person}")

輸出

代碼語言:javascript代碼運行次數:0運行復制

person = Person(name='Jack', age=29, sex='M')

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

代碼語言:javascript代碼運行次數:0運行復制

class Person1 {    lateinit var name: String // lateinit 關鍵字表示該屬性延遲初始化    var age: Int = 0  // lateinit 關鍵字不能修飾 primitive 類型    lateinit var sex: String    override fun toString(): String {        return "Person1(name='$name', age=$age, sex='$sex')"    }}

我們可以在代碼中這樣創建Person1的實例對象

代碼語言:javascript代碼運行次數:0運行復制

    val person1 = Person1()    person1.name = "Jack"    person1.age = 29    person1.sex = "M"    println("person1 = ${person1}")

輸出

代碼語言:javascript代碼運行次數:0運行復制

person1 = Person1(name='Jack', age=29, sex='M')

如果我們想聲明一個具有多種構造方式的類,可以使用 constructor 關鍵字聲明構造函數,示例代碼如下

代碼語言:javascript代碼運行次數:0運行復制

class Person2() { // 無參的主構造函數    lateinit var name: String    var age: Int = 0    lateinit var sex: String    constructor(name: String) : this() { // this 關鍵字指向當前類對象實例        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')"    }}

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

代碼語言:javascript代碼運行次數:0運行復制

class Person2 {    lateinit var name: String    var age: Int = 0    lateinit var sex: String}

自動生成構造函數的操作示意圖

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

第4章 類與面向對象編程第4章 類與面向對象編程

右擊鼠標操作

點擊之后,跳出對話框:生成次級構造函數

第4章 類與面向對象編程第4章 類與面向對象編程

選擇Generate

選擇構造函數的參數

第4章 類與面向對象編程第4章 類與面向對象編程

生成次級構造函數

選中相應的屬性,點擊OK,即可生成。

一個屬性都不選,生成

代碼語言:javascript代碼運行次數:0運行復制

constructor()

選擇一個 name 屬性,生成

代碼語言:javascript代碼運行次數:0運行復制

    constructor(name: String) {        this.name = name    }

選擇name,age屬性生成

代碼語言:javascript代碼運行次數:0運行復制

    constructor(name: String, age: Int) : this(name) {        this.name = name        this.age = age    }

3個屬性都選擇,生成

代碼語言:javascript代碼運行次數:0運行復制

    constructor(name: String, age: Int, sex: String) : this(name, age) {        this.name = name        this.age = age        this.sex = sex    }

最后,我們可以在代碼中這樣創建Person2的實例對象

代碼語言:javascript代碼運行次數:0運行復制

    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}")

實際上,我們在編程實踐中用到最多的構造函數,還是這個

代碼語言:javascript代碼運行次數:0運行復制

class Person(var name: String, var age: Int, var sex: String)

而當確實需要通過比較復雜的邏輯來構建一個對象的時候,可采用構建者(Builder)模式來實現。

4.3 抽象類與接口

抽象類表示“is-a”的關系,而接口所代表的是“has-a”的關系。

抽象類用來表征問題領域的抽象概念。所有編程語言都提供抽象機制。機器語言是對機器的模仿抽象,匯編語言是對機器語言的高層次抽象,高級語言(Fortran,C,Basic等)是對匯編的高層次抽象。而我們這里所說的面向對象編程語言是對過程函數的高層次封裝。這個過程如下圖所示

第4章 類與面向對象編程第4章 類與面向對象編程

編程語言的抽象機制

抽象類和接口是Kotlin語言中兩種不同的抽象概念,他們的存在對多態提供了非常好的支持。這個機制跟Java相同。

4.3.1 抽象類與抽象成員

抽象是相對于具象而言。例如設計一個圖形編輯軟件,問題領域中存在著長方形(Rectangle)、圓形(Circle)、三角形(Triangle)等這樣一些具體概念,它們是具象。但是它們又都屬于形狀(Shape)這樣一個抽象的概念。它們的關系如下圖所示

第4章 類與面向對象編程第4章 類與面向對象編程

形狀Shape的抽象繼承關系

對應的Kotlin代碼如下

代碼語言:javascript代碼運行次數:0運行復制

package com.easy.kotlinabstract class Shapeclass Rectangle : Shape() // 繼承類的語法是使用冒號 : , 父類需要在這里使用構造函數初始化class Circle : Shape()class Triangle : Shape()

因為抽象的概念在問題領域中沒有對應的具體概念,所以抽象類是不能夠實例化的。下面的代碼編譯器會報錯

代碼語言:javascript代碼運行次數:0運行復制

val s = Shape() // 編譯不通過!不能實例化抽象類

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

代碼語言:javascript代碼運行次數:0運行復制

val r = Rectangle()println(r is Shape) // true

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

代碼語言:javascript代碼運行次數:0運行復制

abstract class Shape {    abstract var width: Double    abstract var heigth: Double    abstract var radius: Double    abstract fun area(): Double}

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

代碼語言:javascript代碼運行次數:0運行復制

class Rectangle(override var width: Double, override var heigth: Double, override var radius: Double) : Shape() { // 聲明類的同時也聲明了構造函數    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    }}

其中,override 是覆蓋寫父類屬性和函數的關鍵字。

在代碼中這樣調用具體實現的類的函數

代碼語言:javascript代碼運行次數:0運行復制

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}</string>

抽象類中可以有帶實現的函數,例如我們在抽象類Shape中添加一個函數onClick()

代碼語言:javascript代碼運行次數:0運行復制

abstract class Shape {    ...    fun onClick() { // 默認是final的,不可被覆蓋重寫        println("I am Clicked!")    }}

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

代碼語言:javascript代碼運行次數:0運行復制

    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!

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

代碼語言:javascript代碼運行次數:0運行復制

abstract class Shape {    ...    open fun onClick() {        println("I am Clicked!")    }}

在子類中這樣覆蓋重寫

代碼語言:javascript代碼運行次數:0運行復制

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()}</string>

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

上面的代碼運行輸出

代碼語言:javascript代碼運行次數:0運行復制

12.0Rectangle is Clicked!

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

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

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

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

4.3.2 接口

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

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

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

和Java類似,Kotlin使用interface作為接口的關鍵詞:

代碼語言:javascript代碼運行次數:0運行復制

Interface ProjectService

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

代碼語言:javascript代碼運行次數:0運行復制

interface ProjectService {    val name: String    val owner: String    fun save(project: Project)    fun print() {        println("I am project")    }}

接口是沒有構造函數的。我們使用冒號: 語法來實現一個接口,如果有多個用,逗號隔開:

代碼語言:javascript代碼運行次數:0運行復制

class ProjectServiceImpl : ProjectService // 跟繼承抽象類語法一樣,使用冒號class ProjectMilestoneServiceImpl : ProjectService, MilestoneService // 實現多個接口使用逗號( ,) 隔開

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

第4章 類與面向對象編程第4章 類與面向對象編程

覆蓋沖突

這個時候,我們可以使用下面的語法來調用:

代碼語言:javascript代碼運行次數:0運行復制

super<projectservice>.print()super<milestoneservice>.print()</milestoneservice></projectservice>

4.4 Object對象

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

Kotlin中沒有 靜態屬性和方法,但是可以使用關鍵字 object 聲明一個object 單例對象:

代碼語言:javascript代碼運行次數:0運行復制

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的靜態類一樣的調用形式    println(User.password)    User.hello()}</string>

Kotlin中還提供了 伴生對象 ,用companion object關鍵字聲明:

代碼語言:javascript代碼運行次數:0運行復制

class DataProcessor {    companion object DataProcessor {        fun process() {            println("I am processing data ...")        }    }}fun main(args: Array<string>) {    DataProcessor.process() // I am processing data ...}</string>

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

4.5 數據類

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

使用關鍵字為 data class 創建一個只包含數據的類:

代碼語言:javascript代碼運行次數:0運行復制

data class LoginUser(val username: String, val password: String)

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

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

第4章 類與面向對象編程第4章 類與面向對象編程

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

點擊Decompile

第4章 類與面向對象編程第4章 類與面向對象編程

點擊Decompile

反編譯之后的Java代碼

第4章 類與面向對象編程第4章 類與面向對象編程

反編譯之后的Java代碼

上面這段反編譯之后的完整的Java代碼是

代碼語言:javascript代碼運行次數:0運行復制

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 &amp; 1) != 0) {         var1 = var0.username;      }      if ((var3 &amp; 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) &amp;&amp; Intrinsics.areEqual(this.password, var2.password)) {               return true;            }         }         return false;      } else {         return true;      }   }}

編譯器會從主構造函數中聲明的屬性,自動創建以下函數:

equals() / hashCode() 函數toString() 格式為”LoginUser(username=” + this.username + “, password=” + this.password + “)”component1(),component2() 函數返回對應下標的屬性值,按聲明順序排列copy() 函數: 根據舊對象屬性重新 new LoginUser(username, password) 一個對象出來

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

數據類有如下限制:

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

另外,數據類可以在解構聲明中使用:

代碼語言:javascript代碼運行次數:0運行復制

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}</string>

Kotlin 標準庫提供了 Pair 和 Triple數據類 。

4.6 注解

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

代碼語言:javascript代碼運行次數:0運行復制

@Retention(AnnotationRetention.RUNTIME)@Target(AnnotationTarget.CLASS)internal annotation class Metadata

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

代碼語言:javascript代碼運行次數:0運行復制

annotation class Suspendable // Java中使用的是@interface Suspendable

那么,編譯器會生成對應的元數據信息

代碼語言:javascript代碼運行次數:0運行復制

@Retention(RetentionPolicy.RUNTIME)@Metadata(   mv = {1, 1, 7},   bv = {1, 0, 2},   k = 1,   d1 = {"u0000nnu0002u0018u0002nu0002u0010u001bnu0000bu0086u0002u0018u00002u00020u0001Bu0000¨u0006u0002"},   d2 = {"Lcom/easy/kotlin/Suspendable;", "", "production sources for module kotlin_tutorials_main"})public @interface Suspendable {}

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

代碼語言:javascript代碼運行次數:0運行復制

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)}</image></image>

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

代碼語言:javascript代碼運行次數:0運行復制

@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")    }}

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

4.7 枚舉

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

代碼語言:javascript代碼運行次數:0運行復制

enum class Direction {    NORTH, SOUTH, WEST, EAST // 每個枚舉常量都是一個對象, 用逗號分隔}

相比于字符串常量,使用枚舉能夠實現類型安全。枚舉類有兩個內置的屬性:

代碼語言:javascript代碼運行次數:0運行復制

    public final val name: String    public final val ordinal: Int

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

代碼語言:javascript代碼運行次數:0運行復制

&gt;&gt;&gt; val north = Direction.NORTH&gt;&gt;&gt; north.nameNORTH&gt;&gt;&gt; north.ordinal0&gt;&gt;&gt; north is Directiontrue

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

代碼語言:javascript代碼運行次數:0運行復制

enum class Color(val rgb: Int) {    RED(0xFF0000),    GREEN(0x00FF00),    BLUE(0x0000FF)}

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

代碼語言:javascript代碼運行次數:0運行復制

&gt;&gt;&gt; val c = Color.GREEN&gt;&gt;&gt; cGREEN&gt;&gt;&gt; c.rgb65280&gt;&gt;&gt; c.ordinal1&gt;&gt;&gt; c.nameGREEN

4.8 內部類4.8.1 普通嵌套類

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

代碼語言:javascript代碼運行次數:0運行復制

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            }        }    }}

測試代碼:

代碼語言:javascript代碼運行次數:0運行復制

    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()

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

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

代碼語言:javascript代碼運行次數:0運行復制

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            }        }}}

4.8.2 嵌套內部類

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

代碼語言:javascript代碼運行次數:0運行復制

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()}</string>

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

4.8.3 匿名內部類

匿名內部類,就是沒有名字的內部類。既然是內部類,那么它自然也是可以訪問外部類的變量的。

我們使用對象表達式創建一個匿名內部類實例:

代碼語言:javascript代碼運行次數:0運行復制

class NestedClassesDemo {    class AnonymousInnerClassDemo {        var isRunning = false        fun doRun() {            Thread(object : Runnable { // 匿名內部類                override fun run() {                    isRunning = true                    println("doRun : i am running, isRunning = $isRunning")                }            }).start()        }    }}

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

代碼語言:javascript代碼運行次數:0運行復制

@functionalInterfacepublic interface Runnable {    public abstract void run();}

我們可以使用Lambda表達式創建它,下面的幾種寫法都是可以的:

代碼語言:javascript代碼運行次數:0運行復制

            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()            }

更多關于Lambda表達式以及函數式編程相關內容,我們將在下一章節中介紹。

本章小結

本章我們介紹了Kotlin面向對象編程的特性: 類與構造函數、抽象類與接口、繼承與組合等知識,同時介紹了Kotlin中的注解類、枚舉類、數據類、嵌套類、內部類、匿名內部類、單例object對象等特性類。

總的來說,在面向對象編程范式的支持上,Kotlin相比于Java增加不少有趣的功能與特性支持,這使得我們代碼寫起來更加方便快捷了。

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

相關閱讀

主站蜘蛛池模板: 国产精品人娇在线内谢 | 久久精品国产99国产精品小说 | 四虎影院观看视频在线观看 | 亚洲大香伊人蕉在人依线 | 国产一二三区精品 | 日本一区二区三区精品国产 | 国产欧美一区二区三区精品 | 伊人影院综合在线 | 狠色狠狠色狠狠狠色综合久久 | 福利二区| 成年网站视频在线观看 | 精品偷自拍另类在线观看 | 色狠狠色狠狠综合天天 | 欧美日韩国产58香蕉在线视频 | 亚洲精品国产高清嫩草影院 | 米奇狠狠 | 三级全黄的视频在线观看 | 99999久久久久久亚洲 | 亚洲国产精品综合久久 | 国产高清在线精品一区二区三区 | α片毛片 | 啪啪日韩 | 久热国产视频 | 精品女同一区二区三区在线 | 三级精品视频在线播放 | 六月丁香婷婷色狠狠久久 | 激情综合在线 | 色狠狠狠色噜噜噜综合网 | 一区在线视频 | 国产高清成人 | 国产精品亚洲专区一区 | 天天做天天爱天天爽天天综合 | 亚洲成人免费网址 | 久久精品国产主播一区二区 | 黄色片中国 | 国产一区三区二区中文在线 | 久久国内精品自在自线观看 | 国产精品久久久久亚洲 | 亚洲国产精品67194成人 | 久久综合色之久久综合 | 久久伊人一区二区三区四区 |