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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                原文地址:[Kotlin 里那些「不是那么寫的」](https://kaixue.io/kotlin-basic-2/) [TOC] 上一篇我們講了 Kotlin 上手最基礎的三個點:變量、函數和類型。大家都聽說過,**Kotlin 完全兼容 Java,這個意思是用 Java 寫出來的代碼和 Kotlin 可以完美交互,而不是說你用 Java 的寫法去寫 Kotlin 就完全沒問題,這個是不行的**。這期內容我們就講一下,Kotlin 里那些「不 Java」的寫法。 ## Constructor 上一篇中簡單介紹了 Kotlin 的構造器,這一節具體看看 Kotlin 的構造器和 Java 有什么不一樣的地方: * Java ~~~java ?? public class User { int id; String name; ?? ?? public User(int id, String name) { this.id = id; this.name = name; } } ~~~ * Kotlin ~~~kotlin ??? class User { val id: Int val name: String ?? constructor(id: Int, name: String) { //?? 沒有 public this.id = id this.name = name } } ~~~ 可以發現有兩點不同: * Java 中構造器和類同名,**Kotlin 中使用`constructor`表示**。 * **Kotlin 中構造器沒有 public 修飾,因為默認可見性就是公開的**(關于可見性修飾符這里先不展開,后面會講到)。 ### init **除了構造器,Java 里常常配合一起使用的 init 代碼塊,在 Kotlin 里的寫法也有了一點點改變:你需要給它加一個`init`前綴**。 * Java ~~~java ?? public class User { ?? { // 初始化代碼塊,先于下面的構造器執行 } public User() { } } ~~~ * Kotlin ~~~kotlin ??? class User { ?? init { // 初始化代碼塊,先于下面的構造器執行 } constructor() { } } ~~~ 正如上面標注的那樣,Kotlin 的 init 代碼塊和 Java 一樣,都在實例化時執行,并且執行順序都在構造器之前。 上一篇提到,Java 的類如果不加 final 關鍵字,默認是可以被繼承的,而** Kotlin 的類默認就是 final 的**。在 Java 里 final 還可以用來修飾變量,接下來讓我們看看 Kotlin 是如何實現類似功能的。 ## final Kotlin 中的`val`和 Java 中的`final`類似,表示只讀變量,不能修改。這里分別從成員變量、參數和局部變量來和 Java 做對比: * Java ~~~java ?? ?? final int final1 = 1; ?? void method(final String final2) { ?? final String final3 = "The parameter is " + final2; } ~~~ * Kotlin ~~~kotlin ??? ?? val fina1 = 1 // ?? 參數是沒有 val 的 fun method(final2: String) { ?? val final3 = "The parameter is " + final2 } ~~~ 可以看到不同點主要有: * final 變成了 val。 * **Kotlin 函數參數默認是 val 類型,所以參數前不需要寫 val 關鍵字,Kotlin 里這樣設計的原因是保證了參數不會被修改**,而 Java 的參數可修改(默認沒 final 修飾)會增加出錯的概率。 上一期說過,`var`是 variable 的縮寫,`val`是 value 的縮寫。 其實我們寫 Java 代碼的時候,很少會有人用`final`,但`final`用來修飾變量其實是很有用的,但大家都不用;可你如果去看看國內國外的人寫的 Kotlin 代碼,你會發現很多人的代碼里都會有一堆的`val`。為什么?因為`final`寫起來比`val`麻煩一點:我需要多寫一個單詞。雖然只麻煩這一點點,但就導致很多人不寫。 這就是一件很有意思的事:從`final`到`val`,只是方便了一點點,但卻讓它的使用頻率有了巨大的改變。這種改變是會影響到代碼質量的:在該加限制的地方加上限制,就可以減少代碼出錯的概率。 ### `val`自定義 getter 不過`val`和`final`還是有一點區別的,**雖然`val`修飾的變量不能二次賦值,但可以通過自定義變量的 getter 函數,讓變量每次被訪問時,返回動態獲取的值**: ~~~kotlin ??? ?? val size: Int get() { // ?? 每次獲取 size 值時都會執行 items.size return items.size } ~~~ 不過這個屬于`val`的另外一種用法,大部分情況下`val`還是對應于 Java 中的`final`使用的。 ## static property / function 剛才說到大家都不喜歡寫`final`對吧?但有一種場景,大家是最喜歡用`final`的:常量。 ~~~java ?? public static final String CONST_STRING = "A String"; ~~~ 在 Java 里面寫常量,我們用的是`static`+`final`。而在 Kotlin 里面,除了`final`的寫法不一樣,`static`的寫法也不一樣,而且是更不一樣。確切地說:**在`Kotlin`里,靜態變量和靜態方法這兩個概念被去除了**。 那如果想在 Kotlin 中像 Java 一樣通過類直接引用該怎么辦呢?Kotlin 的答案是`companion object`(伴生對象): ~~~kotlin ??? class Sample { ... ?? companion object { val anotherString = "Another String" } } ~~~ 為啥 Kotlin 越改越復雜了?不著急,我們先看看`object`是個什么東西。 ### `object` Kotlin 里的`object`——首字母小寫的,不是大寫,Java 里的`Object`在 Kotlin 里不用了。 >[info] Java 中的`Object`在 Kotlin 中變成了`Any`,和`Object`作用一樣:作為所有類的基類。 **而`object`不是類,像`class`一樣在 Kotlin 中屬于關鍵字**: ~~~kotlin ??? object Sample { val name = "A name" } ~~~ 它的意思很直接:**創建一個類,并且創建一個這個類的對象**。這個就是`object`的意思:對象。 在代碼中如果要使用這個對象,直接通過它的類名就可以訪問: ~~~kotlin ??? Sample.name ~~~ **當你給一個類使用了object關鍵字,這個類的所有方法全都相當于靜態方法**。因為它本質上相當于一個單例對象,所以沒得選,全部都是直接用類名訪問的。 這不就是單例么,所以**在 Kotlin 中創建單例不用像 Java 中那么復雜,只需要把`class`換成`object`就可以了**。 * 單例類 我們看一個單例的例子,分別用 Java 和 Kotlin 實現: * Java 中實現單例類(非線程安全): ~~~java ?? public class A { private static A sInstance; public static A getInstance() { if (sInstance == null) { sInstance = new A(); } return sInstance; } // ??還有很多模板代碼 ... } ~~~ 可以看到 Java 中為了實現單例類寫了大量的模版代碼,稍顯繁瑣。 * Kotlin 中實現單例類: ~~~kotlin ??? // ?? class 替換成了 object object A { val number: Int = 1 fun method() { println("A.method()") } } ~~~ 和 Java 相比的不同點有: * 和類的定義類似,但是把`class`換成了`object`。 * 不需要額外維護一個實例變量`sInstance`。 * 不需要「保證實例只創建一次」的`getInstance()`方法。 相比 Java 的實現簡單多了。 >[info] 這種通過`object`實現的單例是一個餓漢式的單例,并且實現了線程安全。 * 繼承類和實現接口 Kotlin 中不僅類可以繼承別的類,可以實現接口,`object`也可以: ~~~kotlin ??? open class A { open fun method() { ... } } interface B { fun interfaceMethod() } ?? ?? ?? object C : A(), B { override fun method() { ... } override fun interfaceMethod() { ... } } ~~~ **為什么 object 可以實現接口呢?簡單來講 object 其實是把兩步合并成了一步,既有 class 關鍵字的功能,又實現了單例,這樣就容易理解了**。 * 匿名類 另外,Kotlin 還可以創建 Java 中的匿名類,只是寫法上有點不同: * Java: ~~~java ?? ?? ViewPager.SimpleOnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() { @Override // ?? public void onPageSelected(int position) { // override } }; ~~~ * Kotlin: ~~~kotlin ??? val listener = object: ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { // override } } ~~~ 和 Java 創建匿名類的方式很相似,只不過把`new`換成了`object:`: * Java 中`new`用來創建一個匿名類的對象 * **Kotlin 中`object:`也可以用來創建匿名類的對象** 這里的`new` 和`object:`修飾的都是接口或者抽象類。 ### `companion object`(伴生對象) **用`object`修飾的對象中的變量和函數都是靜態的,但有時候,我們只想讓類中的一部分函數和變量是靜態的該怎么做呢**: ~~~kotlin ??? class A { ?? object B { var c: Int = 0 } } ~~~ 如上,可以在類中創建一個對象,把需要靜態的變量或函數放在內部對象 B 中,外部可以通過如下的方式調用該靜態變量: ~~~kotlin ??? A.B.c ?? ~~~ **類中嵌套的對象可以用`companion`修飾**: ~~~kotlin ??? class A { ?? companion object B { var c: Int = 0 } } ~~~ **`companion`可以理解為伴隨、伴生,表示修飾的對象和外部類綁定**。 但這里有一個小限制:**一個類中最多只可以有一個伴生對象,但可以有多個嵌套對象**。就像皇帝后宮佳麗三千,但皇后只有一個。 這樣的好處是調用的時候可以省掉對象名: ~~~kotlin ??? A.c // ?? B 沒了 ~~~ 所以,當有`companion`修飾時,對象的名字也可以省略掉: ~~~kotlin ??? class A { // ?? B 沒了 companion object { var c: Int = 0 } } ~~~ 這就是這節最開始講到的,**Java 靜態變量和方法的等價寫法:`companion object`變量和函數**。 * 靜態初始化 Java 中的靜態變量和方法,在 Kotlin 中都放在了`companion object`中。因此 **Java 中的靜態初始化在 Kotlin 中自然也是放在`companion object`中的,像類的初始化代碼一樣,由`init`和一對大括號表示**: ~~~kotlin ??? class Sample { ?? companion object { ?? init { ... } } } ~~~ ### top-level property / function 聲明 **除了靜態函數這種簡便的調用方式,Kotlin 還有更方便的東西:「`top-level declaration`頂層聲明」。其實就是把屬性和函數的聲明不寫在`class`里面,這個在 Kotlin 里是允許的**: ~~~kotlin ??? package com.hencoder.plus // ?? 屬于 package,不在 class/object 內 fun topLevelFuncion() { } ~~~ **這樣寫的屬性和函數,不屬于任何`class`,而是直接屬于`package`,它和靜態變量、靜態函數一樣是全局的,但用起來更方便:你在其它地方用的時候,就連類名都不用寫**: ~~~kotlin ??? import com.hencoder.plus.topLevelFunction // ?? 直接 import 函數 topLevelFunction() ~~~ 寫在頂級的函數或者變量有個好處:在 Android Studio 中寫代碼時,IDE 很容易根據你寫的函數前幾個字母自動聯想出相應的函數。這樣提高了寫代碼的效率,而且可以減少項目中的重復代碼。 * **命名相同的頂級函數** 頂級函數不寫在類中可能有一個問題:**如果在不同文件中聲明命名相同的函數,使用的時候會不會混淆**?來看一個例子: * 在`org.kotlinmaster.library`包下有一個函數 method: ~~~kotlin ??? package org.kotlinmaster.library1 ?? fun method() { println("library1 method()") } ~~~ * 在`org.kotlinmaster.library2`包下有一個同名函數: ~~~kotlin ??? package org.kotlinmaster.library2 ?? fun method() { println("library2 method()") } ~~~ **在使用的時候如果同時調用這兩個同名函數會怎么樣**: ~~~kotlin ??? import org.kotlinmaster.library1.method ?? fun test() { method() ?? org.kotlinmaster.library2.method() } ~~~ **可以看到當出現兩個同名頂級函數時,IDE 會自動加上包前綴來區分,這也印證了「頂級函數屬于包」的特性**。 ### 對比 那在實際使用中,在`object`(靜態)、`companion object`(局部靜態)和 top-level 中該選擇哪一個呢?簡單來說按照下面這幾個原則判斷: * **如果想寫工具類的功能,直接創建文件,寫 top-level「頂層」函數**。 * 像TAG這種只是針對類的,而不是針對外部使用者的屬性或者方法,就用`object`或`companion object`。 * 如果需要繼承別的類或者實現接口,就用`object`或`companion object`。 **總結**:簡單地判斷原則:能寫在 top-level「頂層」函數就用 top-level,而`object`或`companion object`看情況按需使用。 ## 常量 Java 中,除了上面講到的的靜態變量和方法會用到`static`,聲明常量時也會用到,那 Kotlin 中聲明常量會有什么變化呢? * Java 中聲明常量: ~~~java ?? public class Sample { ?? ?? public static final int CONST_NUMBER = 1; } ~~~ * Kotlin 中聲明常量: ~~~kotlin ??? class Sample { companion object { ?? // ?? const val CONST_NUMBER = 1 } } const val CONST_SECOND_NUMBER = 2 ~~~ 發現不同點有: * **Kotlin 的常量必須聲明在對象(包括伴生對象)或者「top-level 頂層」中,因為常量是靜態的**。 * **Kotlin 新增了修飾常量的`const`關鍵字**。 除此之外還有一個區別: * **Kotlin 中只有基本類型和 String 類型可以聲明成常量**。 原因是 **Kotlin 中的常量指的是 「compile-time constant 編譯時常量」,它的意思是「編譯器在編譯的時候就知道這個東西在每個調用處的實際值」,因此可以在編譯時直接把這個值硬編碼到代碼里使用的地方**。 而非基本和 String 類型的變量,可以通過調用對象的方法或變量改變對象內部的值,這樣這個變量就不是常量了,來看一個 Java 的例子,比如一個 User 類: ~~~java ?? public class User { int id; // ?? 可修改 String name; // ?? 可修改 public User(int id, String name) { this.id = id; this.name = name; } } ~~~ 在使用的地方聲明一個`static final`的 User 實例`user`,它是不能二次賦值的: ~~~java ?? static final User user = new User(123, "Zhangsan"); ?? ?? ~~~ 但是可以通過訪問這個`user`實例的成員變量改變它的值: ~~~java ?? user.name = "Lisi"; ?? ~~~ 所以 **Java 中的常量可以認為是「偽常量」,因為可以通過上面這種方式改變它內部的值。而 Kotlin 的常量因為限制類型必須是基本類型,所以不存在這種問題,更符合常量的定義**。 前面講的`val`「只讀變量」和靜態變量都是針對單個變量來說的,接下來我們看看編程中另外一個常見的主題:數組和集合。 ## 數組和集合 ### 數組 聲明一個 String 數組: * Java 中的寫法: ~~~java ?? String[] strs = {"a", "b", "c"}; ?? ?? ~~~ * Kotlin 中的寫法: ~~~kotlin ??? val strs: Array<String> = arrayOf("a", "b", "c") ?? ?? ~~~ 可以看到 Kotlin 中的數組是一個擁有泛型的類,創建函數也是泛型函數,和集合數據類型一樣。 >[info] 針對泛型的知識點,我們在后面的文章會講,這里就先按照 Java 泛型來理解。 **將數組泛型化有什么好處呢?對數組的操作可以像集合一樣功能更強大,由于泛型化,Kotlin 可以給數組增加很多有用的工具函數**: * `get() / set()` * `contains()` * `first()` * `find()` 這樣數組的實用性就大大增加了。 * 取值和修改 Kotlin 中獲取或者設置數組元素和 Java 一樣,可以使用方括號加下標的方式索引: ~~~kotlin ??? println(strs[0]) ?? ?? strs[1] = "B" ~~~ * **不支持協變** Kotlin 的數組編譯成字節碼時使用的仍然是 Java 的數組,但在語言層面是泛型實現,這樣會失去協變 (covariance) 特性,就是**子類數組對象不能賦值給父類的數組變量**: * Kotlin ~~~kotlin ??? val strs: Array<String> = arrayOf("a", "b", "c") ?? val anys: Array<Any> = strs // compile-error: Type mismatch ?? ~~~ * 而這在 Java 中是可以的: ~~~java ?? String[] strs = {"a", "b", "c"}; ?? Object[] objs = strs; // success ?? ~~~ 關于協變的問題,這里就先不展開了,后面講泛型的時候會提到。 ### 集合 Kotlin 和 Java 一樣有三種集合類型:List、Set 和 Map,它們的含義分別如下: * `List`以固定順序存儲一組元素,元素可以重復。(**有序,可重復**) * `Set`存儲一組互不相等的元素,通常沒有固定順序。(**無序,不可重復**) * `Map`存儲 鍵-值 對的數據集合,鍵互不相等,但不同的鍵可以對應相同的值。 從 Java 到 Kotlin,這三種集合類型的使用有哪些變化呢?我們依次看看。 * List * Java 中創建一個列表: ~~~java ?? List<String> strList = new ArrayList<>(); strList.add("a"); strList.add("b"); strList.add("c"); // ?? 添加元素繁瑣 ~~~ * Kotlin 中創建一個列表: ~~~kotlin ??? val strList = listOf("a", "b", "c") ~~~ 首先能看到的是 Kotlin 中創建一個`List`特別的簡單,有點像創建數組的代碼。而且**Kotlin 中的`List`多了一個特性:支持 covariant(協變)。也就是說,可以把子類的`List`賦值給父類的`List`變量**: * Kotlin: ~~~kotlin ??? val strs: List<String> = listOf("a", "b", "c") ?? val anys: List<Any> = strs // success ?? ~~~ * 而這在 Java 中是會報錯的: ~~~java ?? List<String> strList = new ArrayList<>(); ?? List<Object> objList = strList; // ?? compile error: incompatible types ?? ~~~ **對于協變的支持與否,`List`和數組剛好反過來了**。關于協變,這里只需結合例子簡單了解下,后面的文章會對它展開討論。 * 和數組的區別 Kotlin 中數組和 MutableList 的 API 是非常像的,主要的區別是數組的元素個數不能變。那在什么時候用數組呢? * 這個問題在 Java 中就存在了,數組和`List`的功能類似,`List`的功能更多一些,直覺上應該用`List`。但數組也不是沒有優勢,基本類型 (`int[]`、`float[]`) 的數組不用自動裝箱,性能好一點。 * 在 Kotlin 中也是同樣的道理,**在一些性能需求比較苛刻的場景,并且元素類型是基本類型時,用數組好一點**。不過這里要注意一點,Kotlin 中要用專門的基本類型數組類 (`IntArray` `FloatArray` `LongArray`) 才可以免于裝箱。也就是說**元素不是基本類型時,相比`Array`,用`List`更方便些**。 * Set * Java 中創建一個`Set`: ~~~java ?? Set<String> strSet = new HashSet<>(); strSet.add("a"); strSet.add("b"); strSet.add("c"); ~~~ * Kotlin 中創建相同的`Set`: ~~~kotlin ??? val strSet = setOf("a", "b", "c") ~~~ 和`List`類似,**`Set`同樣具有 covariant(協變)特性**。 * Map * Java 中創建一個`Map`: ~~~java ?? Map<String, Integer> map = new HashMap<>(); map.put("key1", 1); map.put("key2", 2); map.put("key3", 3); map.put("key4", 3); ~~~ * Kotlin 中創建一個`Map`: ~~~kotlin ??? val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3) ~~~ 和上面兩種集合類型相似創建代碼很簡潔。`mapOf`的每個參數表示一個鍵值對,**`to`表示將「鍵」和「值」關聯,這個叫做「中綴表達式」**,這里先不展開,后面的文章會做介紹。 * 取值和修改 * Kotlin 中的 Map 除了和 Java 一樣可以使用`get()`根據鍵獲取對應的值,還可以使用方括號的方式獲取: ~~~kotlin ??? ?? val value1 = map.get("key1") ?? val value2 = map["key2"] ~~~ * 類似的,Kotlin 中也可以用方括號的方式改變`Map`中鍵對應的值: ~~~kotlin ??? ?? val map = mutableMapOf("key1" to 1, "key2" to 2) ?? map.put("key1", 2) ?? map["key1"] = 2 ~~~ 這里用到了「操作符重載」的知識,實現了和數組一樣的「Positional Access Operations」,關于這個概念這里先不展開,后面會講到。 * 可變集合/不可變集合 上面修改`Map`值的例子中,創建函數用的是`mutableMapOf()`而不是`mapOf()`,**因為只有`mutableMapOf()`創建的`Map`才可以修改**。**Kotlin 中集合分為兩種類型:只讀的和可變的**。這里的**只讀有兩層意思**: * **集合的 size 不可變** * **集合中的元素值不可變** 以下是三種集合類型創建不可變和可變實例的例子: * `listOf()`創建不可變的`List`,`mutableListOf()`創建可變的`List`。 * `setOf()`創建不可變的`Set`,`mutableSetOf()`創建可變的`Set`。 * `mapOf()`創建不可變的`Map`,`mutableMapOf()`創建可變的`Map`。 可以看到,有`mutable`前綴的函數創建的是可變的集合,沒有`mutbale`前綴的創建的是不可變的集合,不過不**可變的可以通過`toMutable*()`系函數轉換成可變的集合**: ~~~kotlin ??? val strList = listOf("a", "b", "c") ?? strList.toMutableList() val strSet = setOf("a", "b", "c") ?? strSet.toMutableSet() val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3) ?? map.toMutableMap() ~~~ 然后就可以對集合進行修改了,這里有一點需要注意下: >[success]**`toMutable*()`返回的是一個新建的集合,原有的集合還是不可變的,所以只能對函數返回的集合修改**。 ### `Sequence` **除了集合 Kotlin 還引入了一個新的容器類型`Sequence`,它和`Iterable`一樣用來遍歷一組數據并可以對每個元素進行特定的處理**,先來看看如何創建一個`Sequence`。 * 創建 * 1、類似`listOf()`,使用一組元素創建: ~~~kotlin ??? sequenceOf("a", "b", "c") ~~~ * 2、使用`Iterable`創建: ~~~kotlin ??? val list = listOf("a", "b", "c") list.asSequence() ~~~ 這里的`List`實現了`Iterable`接口。 * 3、使用 lambda 表達式創建: ~~~kotlin ??? // ?? 第一個元素 val sequence = generateSequence(0) { it + 1 } // ?? lambda 表達式,負責生成第二個及以后的元素,it 表示前一個元素 ~~~ 這看起來和`Iterable`一樣呀,為啥要多此一舉使用`Sequence`呢?在下一篇文章中會結合例子展開討論。 ## 可見性修飾符 講完了數據集合,再看看 Kotlin 中的可見性修飾符,Kotlin 中有四種可見性修飾符: * `public` :公開,可見性最大,**哪里都可以引用**。 * `private`:私有,可見性最小,根據聲明位置不同可分為類中可見和文件中可見。 * `protected`:保護,相當于`private`\+ 子類可見。 * `internal`:內部,僅對 module 內可見。 相比 Java 少了一個`default`「包內可見」修飾符,多了一個`internal`「module 內可見」修飾符。這一節結合例子講講 Kotlin 這四種可見性修飾符,以及在 Kotlin 和 Java 中的不同。先來看看`public`: ### `public` Java 中沒寫可見性修飾符時,表示包內可見,只有在同一個`package`內可以引用: ~~~java ?? ?? package org.kotlinmaster.library; // 沒有可見性修飾符 class User { } ~~~ ~~~java ?? // ?? 和上面同一個 package package org.kotlinmaster.library; public class Example { void method() { new User(); // success } } ~~~ ~~~java ?? package org.kotlinmaster; // ?? 和上面不是一個 package import org.kotlinmaster.library.User; ?? public class OtherPackageExample { void method() { new User(); // compile-error: 'org.kotlinmaster.library.User' is not public in 'org.kotlinmaster.library'. Cannot be accessed from outside package } } ~~~ `package`外如果要引用,需要在`class`前加上可見性修飾符`public`表示公開。 **Kotlin 中如果不寫可見性修飾符,就表示公開,和 Java 中`public`修飾符具有相同效果(同樣的代碼在kotlin就可以運行)**。在 Kotlin 中`public`修飾符「可以加,但沒必要」。 ### `@hide` **在 Android 的官方 sdk 中,有一些方法只想對 sdk 內可見,不想開放給用戶使用**(因為這些方法不太穩定,在后續版本中很有可能會修改或刪掉)。為了實現這個特性,會在方法的注釋中添加一個 Javadoc 方法`@hide`,用來限制客戶端訪問: ~~~java ?? /** * @hide ?? */ public void hideMethod() { ... } ~~~ 但這種限制不太嚴格,可以通過反射訪問到限制的方法。針對這個情況,**Kotlin 引進了一個更為嚴格的可見性修飾符:`internal`。** ### `internal` `internal`表示修飾的類、函數僅對 module 內可見,**這里的 module 具體指的是一組共同編譯的 kotlin 文件**,常見的形式有: * Android Studio 里的 module * Maven project >[info] 我們常見的是 Android Studio 中的 module 這種情況,Maven project 僅作了解就好,不用細究。 **`internal`在寫一個 library module 時非常有用,當需要創建一個函數僅開放給 module 內部使用,不想對 library 的使用者可見,這時就應該用`internal`可見性修飾符**。 ### Java 的「包內可見」怎么沒了? Java 的`default`「包內可見」在 Kotlin 中被棄用掉了,Kotlin 中與它最接近的可見性修飾符是`internal`「module 內可見」。為什么會棄用掉包內可見呢?我覺得有這幾個原因: * Kotlin 鼓勵創建 top-level 函數和屬性,一個源碼文件可以包含多個類,使得 Kotlin 的源碼結構更加扁平化,包結構不再像 Java 中那么重要。 * **為了代碼的解耦和可維護性,module 越來越多、越來越小,使得`internal`「module 內可見」已經可以滿足對于代碼封裝的需求**。 ### `protected` * Java 中`protected`表示包內可見 + 子類可見。 * Kotlin 中`protected`表示`private`\+ 子類可見。 Kotlin 相比 Java:`protected`的可見范圍收窄了,原因是 Kotlin 中不再有「包內可見」的概念了,相比 Java 的可見性著眼于`package`,Kotlin 更關心的是 module。 ### `private` * Java 中的`private`表示類中可見,作為內部類時對外部類「可見」。 * **Kotlin 中的`private`表示類中或所在文件內可見,作為內部類時對外部類「不可見」**。 `private`修飾的變量「類中可見」和 「文件中可見」: ~~~kotlin ??? class Sample { private val propertyInClass = 1 // ?? 僅 Sample 類中可見 } private val propertyInFile = "A string." // ?? 范圍更大,整個文件可見 ~~~ `private`修飾內部類的變量時,在 Java 和 Kotlin 中的區別 * 在 Java 中,外部類可以訪問內部類的`private`變量: ~~~java ?? public class Outter { public void method() { Inner inner = new Inner(); ?? int result = inner.number * 2; // success } private class Inner { private int number = 0; } } ~~~ * **在 Kotlin 中,外部類不可以訪問內部類的`private`變量**: ~~~kotlin ??? class Outter { fun method() { val inner = Inner() ?? val result = inner.number * 2 // compile-error: Cannot access 'number': it is private in 'Inner' } class Inner { private val number = 1 } } ~~~ * 可以修飾類和接口 * Java 中一個文件只允許一個外部類,所以`class`和`interface`不允許設置為`private`,因為聲明`private`后無法被外部使用,這樣就沒有意義了。 * **Kotlin 允許同一個文件聲明多個`class`和 top-level 的函數和屬性,所以 Kotlin 中允許類和接口聲明為`private`,因為同個文件中的其它成員可以訪問**: ~~~kotlin ??? private class Sample { val number = 1 fun method() { println("Sample method()") } } // ?? 在同一個文件中,所以可以訪問 val sample = Sample() ~~~ * * * ## 練習題 1. 創建一個 Kotlin 類,這個類需要禁止外部通過構造器創建實例,并提供至少一種實例化方式。 2. 分別用 Array、IntArray、List 實現 「保存 1-100\_000 的數字,并求出這些數字的平均值」,打印出這三種數據結構的執行時間。
                  <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>

                              哎呀哎呀视频在线观看