<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                原文地址:[Kotlin 的變量、函數和類型](https://kaixue.io/kotlin-basic-1/) [TOC] ## 為項目添加 Kotlin 語言的支持 學習 Kotlin 的第一步就是要為項目添加 Kotlin 語言的支持,這非常簡單。 ### 新建支持 Kotlin 的 Android 項目 如果你要新建一個支持 Kotlin 的 Android 項目,只需要如下操作: * File -> New -> New Project … * Choose your project -> Phone and Tablet -> Empty Activity * Configure your project -> Language 選擇 「Kotlin」 別的都和創建一個普通的 Android 項目一樣,創建出的項目就會是基于 Kotlin 的了。 所謂「基于 Kotlin」,意思有兩點: 1. IDE 幫你自動創建出的`MainActivity`是用 Kotlin 寫的: ~~~kotlin package org.kotlinmaster import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { ... } ~~~ >[info]掃一眼就好,不用讀代碼,我們后面都會講。 2. 項目中的 2 個`bulid.gradle`文件比 Java 的 Android 項目多了幾行代碼(以「??」標注),它們的作用是添加 Kotlin 的依賴: * 項目根目錄下的`build.gradle`: ~~~groovy buildscript { ?? ext.kotlin_version = '1.3.41' repositories { ... } dependencies { classpath 'com.android.tools.build:gradle:3.5.0-beta05' ?? classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } ~~~ app 目錄下的`build.gradle`: ~~~groovy apply plugin: 'com.android.application' ?? apply plugin: 'kotlin-android' ... android { ... } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) ?? implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" ... } ~~~ 也就是說,如果你是要創建一個新項目,記得把語言選擇為 Kotlin,項目創建完成后你就可以用 Kotlin 來寫它了。 ### 給現有項目添加 Kotlin 支持 如果是現有的項目要支持 Kotlin,只需要像上面這樣操作,把兩個`build.gradle`中標注的代碼對應貼到你的項目里就可以了。 筆者建議剛開始學習的時候還是新建一個基于 Kotlin 的項目,按照上面的步驟練習一下。 ### 初識 MainActivity.kt 前面我們提到,如果新建的項目是基于 Kotlin 的,IDE 會幫我們創建好`MainActivity`,它其實是有一個`.kt`的文件后綴名(打開的時候可以看到)。 >[info] Kotlin 文件都是以`.kt`結尾的,就像 Java 文件是以`.java`結尾。 我們看看這個`MainActivity.kt`里到底有些什么: ~~~kotlin package org.kotlinmaster ?? import android.os.Bundle ?? import androidx.appcompat.app.AppCompatActivity ?? class MainActivity : AppCompatActivity() { ?? ?? ?? ?? ?? override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } ~~~ 乍一看,「??」標注的`package` `import` `class`這些 Java 里的東西,Kotlin 也有;但是也有一些以「??」標注的在 Java 里是沒見過的。 為了暫時避開這些干擾,我們自己新建一個文件。 * 在新建 Java Class 的入口下面可以看見一個叫 「Kotlin File/Class」 的選項,這就是我們新建 Kotlin 文件的入口 * New Kotlin File/Class * Name: Sample * Kind: Class 創建完成后的`Sample.kt`: ~~~kotlin package org.kotlinmaster class Sample {} ~~~ 這個類僅包含`package`和`class`兩個關鍵字,我們暫時先看成和 Java 差不多(其實真的就是差不多)的概念,這樣就都是我們熟悉的東西了。 接下來,讓我們開始學習基礎語法吧。 * * * ## 變量 ### 變量的聲明與賦值 >[info] 這里講一個 Java 和 Kotlin 命名由來的小插曲。 我們知道 Java 就是著名的爪哇島,爪哇島盛產咖啡,據說就是一群研究出 Java 語言的牛人們在為它命名時由于聞到香濃的咖啡味,遂決定采用此名稱。 Kotlin 來源于芬蘭灣中的 Kotlin 島。 因此,我們在代碼段的開頭以「??」來表示 Java 代碼段,「???」來表示 Kotlin 代碼段。 我們回憶下 Java 里聲明一個 View 類型的變量的寫法: ~~~java ?? View v; ~~~ Kotlin 里聲明一個變量的格式是這樣的: ~~~kotlin ??? var v: View ~~~ 這里有幾處不同: * 有一個`var`關鍵字 * 類型和變量名位置互換了 * 中間是用冒號分隔的 * 結尾沒有分號(對,Kotlin 里面不需要分號) 看上去只是語法格式有些不同,但如果真這么寫,IDE 會報錯: ~~~kotlin ??? class Sample { var v: View // ??這樣寫 IDE 會報如下錯誤 // Property must be initialized or be abstract } ~~~ 這個提示是在說,屬性需要在聲明的同時初始化,除非你把它聲明成抽象的。 * 那什么是屬性呢?這里我們可以簡單類比 Java 的 field 來理解 Kotlin 的 Property,雖然它們其實有些不一樣,Kotlin 的 Property 功能會多些。 * 變量居然還能聲明成抽象的?嗯,這是 Kotlin 的功能,不過這里先不理它,后面會講到。 >[success]注意:這里的示例中的屬性是類中的屬性,如果是函數中的屬性,沒有這種硬性要求,可以出現這種形式:`var a: Float` 屬性為什么要求初始化呢?因為 **Kotlin 的變量是沒有默認值的**,這點不像 Java,Java 的 field 有默認值: ~~~java ?? String name; // ??默認值是 null int count; // ??默認值是 0 ~~~ 但這些 Kotlin 是沒有的。不過其實,Java 也只是 field 有默認值,局部變量也是沒有默認值的,如果不給它初始值也會報錯: ~~~java ?? void run() { int count; count++; // ??IDE 報錯,Variable 'count' might not have been initialized } ~~~ 既然這樣,那我們就給它一個默認值 null 吧,遺憾的是你會發現仍然報錯。 ~~~kotlin ??? class Sample { var v: View = null // ??這樣寫 IDE 仍然會報錯,Null can not be a value of a non-null type View } ~~~ 又不行,IDE 告訴我需要賦一個非空的值給它才行,怎么辦?Java 的那套不管用了。 其實這都是 Kotlin 的空安全設計相關的內容。很多人嘗試上手 Kotlin 之后快速放棄,就是因為搞不明白它的空安全設計,導致代碼各種拒絕編譯,最終只能放棄。所以咱先別急,我先來給你講一下 Kotlin 的空安全設計。 ### Kotlin 的空安全設計 簡單來說就是通過 IDE 的提示來避免調用 null 對象,從而避免 NullPointerException。其實在 androidx 里就有支持的,用一個注解就可以標記變量是否可能為空,然后 IDE 會幫助檢測和提示,我們來看下面這段 Java 代碼: ~~~java ?? @NonNull View view = null; // ??IDE 會提示警告,'null' is assigned to a variable that is annotated with @NotNull ~~~ 而到了 Kotlin 這里,就有了語言級別的默認支持,而且提示的級別從 warning 變成了 error(拒絕編譯): ~~~kotlin ??? var view: View = null // ??IDE 會提示錯誤,Null can not be a value of a non-null type View ~~~ **在 Kotlin 里面,所有的變量默認都是不允許為空的,如果你給它賦值 null,就會報錯**,像上面那樣。 這種有點強硬的要求,其實是很合理的:既然你聲明了一個變量,就是要使用它對吧?那你把它賦值為 null 干嘛?要盡量讓它有可用的值啊。Java 在這方面很寬松,我們成了習慣,但 Kotlin 更強的限制其實在你熟悉了之后,是會減少很多運行時的問題的。 不過,**還是有些場景,變量的值真的無法保證空與否**,比如你要從服務器取一個 JSON 數據,并把它解析成一個 User 對象: ~~~kotlin ??? class User { var name: String = null // ??這樣寫會報錯,但該變量無法保證空與否 } ~~~ 這個時候,空值就是有意義的。對于這些可以為空值的變量,你可以在類型右邊加一個`?`號,解除它的非空限制: ~~~kotlin ??? class User { var name: String? = null } ~~~ **加了問號之后,一個 Kotlin 變量就像 Java 變量一樣沒有非空的限制,自由自在了**。 你除了在初始化的時候可以給它賦值為空值,在代碼里的任何地方也都可以: ~~~kotlin ??? var name: String? = "Mike" ... name = null // ??原來不是空值,賦值為空值 ~~~ 這種類型之后加`?`的寫法,在 Kotlin 里叫**可空類型**。 不過,當我們使用了可空類型的變量后,會有新的問題: 由于對空引用的調用會導致空指針異常,所以 Kotlin 在可空變量直接調用的時候 IDE 會報錯: ~~~kotlin ??? var view: View? = null view.setBackgroundColor(Color.RED) // ??這樣寫會報錯,Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type View? ~~~ 「可能為空」的變量,Kotlin 不允許用。那怎么辦?我們嘗試用之前檢查一下,但似乎 IDE 不接受這種做法: ~~~kotlin ??? if (view != null) { view.setBackgroundColor(Color.RED) // ??這樣寫會報錯,Smart cast to 'View' is impossible, because 'view' is a mutable property that could have been changed by this time } ~~~ 這個報錯的意思是**即使你檢查了非空也不能保證下面調用的時候就是非空,因為在多線程情況下,其他線程可能把它再改成空的**。 那么 Kotlin 里是這么解決這個問題的呢?它用的不是`.`而是`?.`: ~~~kotlin ??? view?.setBackgroundColor(Color.RED) ~~~ **這個寫法同樣會對變量做一次非空確認之后再調用方法,這是 Kotlin 的寫法**,并且它可以**做到線程安全**,因此這種寫法叫做「**safe call**」。 另外還有一種雙感嘆號的用法: ~~~kotlin ??? view!!.setBackgroundColor(Color.RED) ~~~ 意思是告訴編譯器,我保證這里的 view 一定是非空的,編譯器你不要幫我做檢查了,有什么后果我自己承擔。這種「肯定不會為空」的斷言式的調用叫做 「**non-null asserted call**」。**一旦用了非空斷言,實際上和 Java 就沒什么兩樣了,但也就享受不到 Kotlin 的空安全設計帶來的好處(在編譯時做檢查,而不是運行時拋異常)了**。 以上就是 Kotlin 的空安全設計。 理解了它之后再來看變量聲明,跟 Java 雖然完全不一樣,只是寫法上不同而已。 很多人在上手的時候都被變量聲明搞懵,原因就是 Kotlin 的空安全設計所導致的這些報錯: * 變量需要手動初始化,如果不初始化的話會報錯; * 變量默認非空,所以初始化賦值 null 的話報錯,之后再次賦值為 null 也會報錯; * 變量用`?`設置為可空的時候,使用的時候因為「可能為空」又報錯。 明白了空安全設計的原理后,就很容易能夠解決上面的問題了。 關于空安全,最重要的是記住一點:**所謂「可空不可空」,關注的全都是使用的時候,即「這個變量在使用時是否可能為空」**。 另外,Kotlin 的這種空安全設計在與 Java 的互相調用上是完全兼容的,這里的兼容指: * Java 里面的 @Nullable 注解,在 Kotlin 里調用時同樣需要使用`?.`。 ~~~java ?? @Nullable String name; ~~~ ~~~kotlin ??? name?.length ~~~ * Java 里面的 @Nullable 和 @NonNull 注解,在轉換成 Kotlin 后對應的就是可空變量和不可空變量,至于怎么將 Java 代碼轉換為 Kotlin,Android Studio 給我們提供了很方便的工具(但并不完美),后面會講。 ~~~java ?? @Nullable String name; @NonNull String value = "hello"; ~~~ ~~~kotlin ??? var name: String? = null var value: String = "hello" ~~~ 空安全我們講了這么多,但是有些時候我們聲明一個變量是不會讓它為空的,比如 view,其實在實際場景中我們希望它一直是非空的,可空并沒有業務上的實際意義,使用`?.`影響代碼可讀性。 但如果你在`MainActivity`里這么寫: ~~~kotlin ??? class MainActivity : AppCompatActivity() { ?? var view: View = findViewById(R.id.tvContent) } ~~~ 雖然編譯器不會報錯,但程序一旦運行起來就 crash 了,原因是 findViewById() 是在 onCreate 之后才能調用。 那怎么辦呢?其實我們很想告訴編譯器「**我很確定我用的時候絕對不為空,但第一時間我沒法給它賦值**」。 Kotlin 給我們提供了一個選項:**延遲初始化**。 ### 延遲初始化 具體是這么寫的: ~~~kotlin ??? lateinit var view: View ~~~ **這個`lateinit`的意思是:告訴編譯器我沒法第一時間就初始化,但我肯定會在使用它之前完成初始化的**。 它的作用就是讓 IDE 不要對這個變量檢查初始化和報錯。換句話說,**加了這個`lateinit`關鍵字,這個變量的初始化就全靠你自己了,編譯器不幫你檢查了**。 然后我們就可以在 onCreate 中進行初始化了: ~~~kotlin ??? ?? lateinit var view: View override fun onCreate(...) { ... ?? view = findViewById(R.id.tvContent) } ~~~ 哦對了,**延遲初始化對變量的賦值次數沒有限制,你仍然可以在初始化之后再賦其他的值給`view`**。 ### 類型推斷 **Kotlin 有個很方便的地方是,如果你在聲明的時候就賦值,那不寫變量類型也行**: ~~~kotlin ??? var name: String = "Mike" ?? var name = "Mike" ~~~ 這個特性叫做**類型推斷,它跟動態類型是不一樣的**,我們不能像使用 Groovy 或者 JavaScript 那樣使用在 Kotlin 里這么寫: ~~~kotlin ??? var name = "Mike" name = 1 // ??會報錯,The integer literal does not conform to the expected type String ~~~ ~~~groovy // Groovy def a = "haha" a = 1 // ??這種先賦值字符串再賦值數字的方式在 Groovy 里是可以的 ~~~ **「動態類型」是指變量的類型在運行時可以改變;而「類型推斷」是你在代碼里不用寫變量類型,編譯器在編譯的時候會幫你補上。因此,Kotlin 是一門靜態語言**。 除了變量賦值這個場景,類型推斷的其他場景我們之后也會遇到。 ### val 和 var 聲明變量的方式也不止 var 一種,我們還可以使用 val: ~~~kotlin ??? val size = 18 ~~~ val 是 Kotlin 在 Java 的「變量」類型之外,又增加的一種變量類型:**只讀變量。它只能賦值一次,不能修改**。而 **var 是一種可讀可寫變量**。 >[info] var 是 variable 的縮寫,val 是 value 的縮寫。 **val 和 Java 中的 final 類似**: ~~~java ?? final int size = 18; ~~~ 不過**其實它們還是有些不一樣的,這個我們之后再講。總之直接進行重新賦值是不行的**。 ### 可見性 看到這里,我們似乎都沒有在 Kotlin 里看到類似 Java 里的 public、protected、private 這些表示變量可見性的修飾符,**因為在 Kotlin 里變量默認就是public的**,而對于其他可見性修飾符,我們之后會講,這里先不用關心。 至此,我相信你對變量這部分已經了解得差不多了,可以根據前面的例子動手嘗試嘗試。 ## 函數 Kotlin 除了變量聲明外,函數的聲明方式也和 Java 的方法不一樣。Java 的方法(method)在 Kotlin 里叫函數(function),其實沒啥區別,或者說其中的區別我們可以忽略掉。**對任何編程語言來講,變量就是用來存儲數據,而函數就是用來處理數據**。 ### 函數的聲明 我們先來看看 Java 里的方法是怎么寫的: ~~~java ?? Food cook(String name) { ... } ~~~ 而到了 Kotlin,函數的聲明是這樣: ~~~kotlin ??? ?? ?? fun cook(name: String): Food { ... } ~~~ * **以 fun 關鍵字開頭** * **返回值寫在了函數和參數后面** 那如果**沒有返回值**該怎么辦?Java 里是返回 void: ~~~java ?? void main() { ... } ~~~ **Kotlin 里是返回 Unit,并且可以省略**: ~~~kotlin ??? ?? fun main(): Unit {} // Unit 返回類型可以省略 fun main() {} ~~~ **函數參數也可以有可空的控制**,根據前面說的空安全設計,在傳遞時需要注意: ~~~kotlin ??? // ??可空變量傳給不可空參數,報錯,即便它后面賦值了 var myName : String? = "rengwuxian" fun cook(name: String) : Food {} cook(myName) // ??可空變量傳給可空參數,正常運行 var myName : String? = "rengwuxian" fun cook(name: String?) : Food {} cook(myName) // ??不可空變量傳給不可空參數,正常運行 var myName : String = "rengwuxian" fun cook(name: String) : Food {} cook(myName) ~~~ ### 可見性 **函數如果不加可見性修飾符的話,默認的可見范圍和變量一樣也是 public 的,但有一種情況例外,這里簡單提一下,就是遇到了`override`關鍵字的時候**,下面會講到。 ### 屬性的 getter/setter 函數 我們知道,在 Java 里面的 field 經常會帶有 getter/setter 函數: ~~~java ?? public class User { String name; public String getName() { return this.name; } public void setName(String name) { this.name = name; } } ~~~ 它們的作用就是可以自定義函數內部實現來達到「鉤子」的效果,比如下面這種: ~~~java ?? public class User { String name; public String getName() { return this.name + " nb"; } public void setName(String name) { this.name = "Cute " + name; } } ~~~ 在 Kotlin 里,這種 getter / setter 是怎么運作的呢? ~~~kotlin ??? class User { var name = "Mike" fun run() { name = "Mary" // ??的寫法實際上是??這么調用的 // setName("Mary") // 建議自己試試,IDE 的代碼補全功能會在你打出 setn 的時候直接提示 name 而不是 setName println(name) // ??的寫法實際上是??這么調用的 // print(getName()) // IDE 的代碼補全功能會在你打出 getn 的時候直接提示 name 而不是 getName } } ~~~ 那么我們如何來操作前面提到的「鉤子」呢?看下面這段代碼: ~~~kotlin ??? class User { var name = "Mike" ?? get() { return field + " nb" } ?? ?? set(value) { field = "Cute " + value } } ~~~ 格式上和 Java 有一些區別: * getter / setter 函數有了專門的關鍵字 get 和 set * getter / setter 函數位于 var 所聲明的變量下面 * setter 函數參數是 value 除此之外還多了一個叫 field 的東西。這個東西叫做「**Backing Field**」,中文翻譯是**幕后字段**或**后備字段**(馬云背后的女人??)。具體來說,你的這個代碼: ~~~kotlin ??? class Kotlin { var name = "kaixue.io" } ~~~ 在編譯后的字節碼大致等價于這樣的 Java 代碼: ~~~java ?? public final class Kotlin { @NotNull private String name = "kaixue.io"; @NotNull public final String getName() { return this.name; } public final void setName(@NotNull String name) { this.name = name; } } ~~~ **上面的那個`String name`就是 Kotlin 幫我們自動創建的一個 Java field。這個 field 對編碼的人不可見,但會自動應用于 getter 和 setter,因此它被命名為Backing Field(backing 的意思是在背后進行支持,例如你闖了大禍,我動用能量來保住你的人頭,我就是在 back you)**。 所以,**雖然 Kotlin 的這個`field`本質上確實是一個 Java 中的 field,但對于 Kotlin 的語法來講,它和 Java 里面的 field 完全不是一個概念。在 Kotlin 里,它相當于每一個 var 內部的一個變量**。 我們前面講過 val 是只讀變量,只讀的意思就是說 val 聲明的變量不能進行重新賦值,也就是說不能調用 setter 函數,因此,**val 聲明的變量是不能重寫 setter 函數的,但它可以重寫 getter 函數**: ~~~kotlin ??? val name = "Mike" get() { return field + " nb" } ~~~ **val 所聲明的只讀變量,在取值的時候仍然可能被修改,這也是和 Java 里的 final 的不同之處**。 關于「鉤子」的作用,除了修改取值和賦值,也可以加一些自己的邏輯,就像我們在 Activity 的生命周期函數里做的事情一樣。 ## 類型 講完了變量和函數,接下來我們可以系統性地學習下 Kotlin 里的類型。 ### 基本類型 **在 Kotlin 中,所有東西都是對象,Kotlin 中使用的基本類型有:數字、字符、布爾值、數組與字符串**。 ~~~kotlin ??? var number: Int = 1 // ??還有 Double Float Long Short Byte 都類似 var c: Char = 'c' var b: Boolean = true var array: IntArray = intArrayOf(1, 2) // ??類似的還有 FloatArray DoubleArray CharArray 等,intArrayOf 是 Kotlin 的 built-in 函數 var str: String = "string" ~~~ 這里有兩個地方和 Java 不太一樣: * **Kotlin 里的 Int 和 Java 里的 int 以及 Integer 不同,主要是在裝箱方面不同**。 Java 里的 int 是 unbox 的,而 Integer 是 box 的: ~~~java ?? int a = 1; Integer b = 2; // ??會被自動裝箱 autoboxing ~~~ **Kotlin 里,Int 是否裝箱根據場合來定**: ~~~kotlin ??? var a: Int = 1 // unbox var b: Int? = 2 // box var list: List<Int> = listOf(1, 2) // box ~~~ Kotlin 在語言層面簡化了 Java 中的 int 和 Integer,但是我們對是否裝箱的場景還是要有一個概念,因為這個**牽涉到程序運行時的性能開銷**。 **因此在日常的使用中,對于 Int 這樣的基本類型,盡量用不可空變量**。 * Java 中的數組和 Kotlin 中的數組的寫法也有區別: ~~~java ?? int[] array = new int[] {1, 2}; ~~~ 而在 Kotlin 里,上面的寫法是這樣的: ~~~kotlin ??? var array: IntArray = intArrayOf(1, 2) // ??這種也是 unbox 的 ~~~ 簡單來說,原先在 Java 里的基本類型,類比到 Kotlin 里面,條件滿足如下之一就**不裝箱**: * **不可空類型**。 * **使用 IntArray、FloatArray 等**。 ### 類和對象 現在可以來看看我們的老朋友`MainActivity`了,重新認識下它: ~~~kotlin ??? class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { ... } } ~~~ 我們可以對比 Java 的代碼來看有哪些不同: ~~~java ?? public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... } } ~~~ * 首先是類的可見性,Java 中的 public 在 Kotlin 中可以省略,**Kotlin 的類默認是 public 的**。 * 類的繼承的寫法,Java 里用的是`extends`,而在 Kotlin 里使用`:`,但其實`:`不僅可以表示繼承,還可以表示 Java 中的`implement`。 舉個例子,假設我們有一個 interface 叫 Impl: ~~~kotlin ??? interface Impl {} ~~~ >[info] Kotlin 里定義一個 interface 和 Java 沒什么區別。 ~~~java ?? public class Main2Activity extends AppCompatActivity implements Impl { } ~~~ ~~~kotlin ??? class MainActivity : AppCompatActivity(), Impl {} ~~~ * 構造方法的寫法不同。 * Java 里省略了默認的構造函數: * ~~~java ?? public class MainActivity extends AppCompatActivity { // ??默認構造函數 public MainActivity() { } } ~~~ * Kotlin 里我們注意到 AppCompatActivity 后面的`()`,這其實也是一種省略的寫法,等價于: * ~~~kotlin ??? class MainActivity constructor() : AppCompatActivity() { ?? } ~~~ 不過其實更像 Java 的寫法是這樣的: ~~~kotlin ??? // ??注意這里 AppCompatActivity 后面沒有 '()' class MainActivity : AppCompatActivity { constructor() { } } ~~~ **Kotlin 把構造函數單獨用了一個`constructor`關鍵字來和其他的普通函數`fun`做區分**。 * override 的不同 * Java 里面`@Override`是注解的形式。 * Kotlin 里的`override`變成了關鍵字。 * Kotlin 省略了`protected`關鍵字,也就是說,**Kotlin 里的`override`函數的可見性是繼承自父類的**。 除了以上這些明顯的不同之外,還有一些不同點從上面的代碼里看不出來,但當你寫一個類去繼承`MainActivity`時就會發現: * **Kotlin 里的 MainActivity 無法繼承**: ~~~kotlin ??? // ??寫法會報錯,This type is final, so it cannot be inherited from class NewActivity: MainActivity() { } ~~~ 原因是 **Kotlin 里的類默認是 final 的**,而 Java 里只有加了`final` 關鍵字的類才是 final 的。 那么有什么辦法解除 final 限制么?我們**可以使用`open`來做這件事**: ~~~kotlin ??? open class MainActivity : AppCompatActivity() {} ~~~ 這樣一來,我們就可以繼承了。 ~~~kotlin ??? class NewActivity: MainActivity() {} ~~~ 但是要注意,**此時 NewActivity 仍然是 final 的**,也就是說,**`open`沒有父類到子類的遺傳性**。 而**剛才說到的`override`是有遺傳性的**: ~~~kotlin ??? class NewActivity : MainActivity() { // ??onCreate 仍然是 override 的 override fun onCreate(savedInstanceState: Bundle?) { ... } } ~~~ **如果要關閉`override`的遺傳性,只需要這樣即可**: ~~~kotlin ??? open class MainActivity : AppCompatActivity() { // ??加了 final 關鍵字,作用和 Java 里面一樣,關閉了 override 的遺傳性 final override fun onCreate(savedInstanceState: Bundle?) { ... } } ~~~ * Kotlin 里除了新增了`open`關鍵字之外,也有和 Java 一樣的`abstract`關鍵字,這倆關鍵字的區別就是`abstract`關鍵字修飾的類無法直接實例化,并且通常來說會和`abstract`修飾的函數一起出現,當然,也可以沒有這個`abstract`函數。 ~~~kotlin ??? abstract class MainActivity : AppCompatActivity() { abstract fun test() } ~~~ **但是子類如果要實例化,還是需要實現這個 abstract 函數的**: ~~~kotlin ??? class NewActivity : MainActivity() { override fun test() {} } ~~~ 當我們聲明好一個類之后,我們就可以實例化它了,實例化在 Java 中使用`new`關鍵字: ~~~java ?? void main() { Activity activity = new NewActivity(); } ~~~ 而**在 Kotlin 中,實例化一個對象更加簡單,沒有`new`關鍵字**: ~~~kotlin ??? fun main() { var activity: Activity = NewActivity() } ~~~ 通過`MainActivity`的學習,我們知道了 Java 和 Kotlin 中關于類的聲明主要關注以下幾個方面: * 類的可見性和開放性 * 構造方法 * 繼承 * override 函數 ### 類型的判斷和強轉 剛才講的實例化的例子中,我們實際上是把子類對象賦值給父類的變量,這個概念在 Java 里叫多態,**Kotlin 也有這個特性,但在實際工作中我們很可能會遇到需要使用子類才有的函數**。 比如我們先在子類中定義一個函數: ~~~kotlin ??? class NewActivity : MainActivity() { fun action() {} } ~~~ 那么接下來這么寫是無法調用該函數的: ~~~kotlin ??? fun main() { var activity: Activity = NewActivity() // ??activity 是無法調用 NewActivity 的 action 方法的 } ~~~ 在 Java 里,需要先使用`instanceof`關鍵字判斷類型,再通過強轉來調用: ~~~java ?? void main() { Activity activity = new NewActivity(); if (activity instanceof NewActivity) { ((NewActivity) activity).action(); } } ~~~ Kotlin 里同樣有類似解決方案,**使用`is`關鍵字進行「類型判斷」,并且因為編譯器能夠進行類型推斷,可以幫助我們省略強轉的寫法**: ~~~kotlin ??? fun main() { var activity: Activity = NewActivity() if (activity is NewActivity) { // ??的強轉由于類型推斷被省略了 activity.action() } } ~~~ 那么能不能**不進行類型判斷,直接進行強轉調用呢?可以使用`as`關鍵字**: ~~~kotlin ??? fun main() { var activity: Activity = NewActivity() (activity as NewActivity).action() } ~~~ 這種寫法如果強轉類型操作是正確的當然沒問題,但**如果強轉成一個錯誤的類型,程序就會拋出一個異常**。 我們**更希望能進行安全的強轉,可以更優雅地處理強轉出錯的情況**。 這一點,**Kotlin 在設計上自然也考慮到了,我們可以使用`as?`來解決**: ~~~kotlin ??? fun main() { var activity: Activity = NewActivity() // ??'(activity as? NewActivity)' 之后是一個可空類型的對象,所以,需要使用 '?.' 來調用 (activity as? NewActivity)?.action() } ~~~ 它的**意思就是說如果強轉成功就執行之后的調用,如果強轉不成功就不執行**。 * * * 好了,關于 Kotlin 的變量、函數和類型的內容就講到這里,給你留 2 道思考題吧 1. 子類重寫父類的`override`函數,能否修改它的可見性?//不能 2. 以下的寫法有什么區別? ~~~kotlin ??? activity as? NewActivity activity as NewActivity? activity as? NewActivity? ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看