當前位置:網站首頁>Stack Overflow 上最熱門的 10 個 Kotlin 問題
Stack Overflow 上最熱門的 10 個 Kotlin 問題
2022-05-14 06:42:04【hi-dhl】
- 譯文地址: https://blog.autsoft.hu/top-10-kotlin…
- 譯者:DHL
- 本文已收錄於倉庫 Technical-Article-Translation
這是 Stack Overflow 上最熱門的幾個 Kotlin 問題,每個問題如果更深入的分析,都可以單獨寫一篇文章,後面我會針對這些問題,在進一步的分析。
通過這篇文章你將學習到以下內容:
Array<Int>
和IntArray
的區別,以及如何選擇Iterable
和Sequence
的區別,以及如何選擇- 常用的 8 種 For 循環遍曆的方法
- 在 Kotlin 中如何使用 SAM 轉換
- 如何聲明一個靜態成員,Java 和 Koltin 進行互操作
- 為什麼 kotlin 中的智能轉換不能用於可變屬性,如何才能解决這個問題
- 當重寫 Java 函數時,如何决定參數的可空性
- 如何在一個文件中使用多個具有相同名稱的擴展函數和類
譯文
Array 和 IntArray 的區別
Array
Array<T>
可以為任何 T 類型存儲固定數量的元素。它和 Int
類型參數一起使用, 例如 Array<Int>
,編譯成 Java 代碼,會生成 Integer[]
實例。我們可以通過 arrayOf
方法創建數組。
val arrayOfInts: Array<Int> = arrayOf(1, 2, 3, 4, 5)
IntArray
IntArray
可以讓我們使用基礎數據類型的數組,編譯成 Java 代碼,會生成 int[]
(其它的基礎類型的數組還有 ByteArray
, CharArray
等等), 我們可以通過 intArrayOf
工廠方法創建數組。
val intArray: IntArray = intArrayOf(1, 2, 3, 4, 5)
什麼時候使用 Array<Int>
或者 IntArray
默認使用 IntArray
,因為它的性能更好,不需要對每個元素進行裝箱。IntArray
進行初始化的時候,默認將每個索引的值初始化為 0,代碼如下所示。
val intArray = IntArray(10)
val arrayOfInts = Array<Int>(5) { i -> i * 2 }
而 Array<Int>
的性能比較差,會對每個元素進行裝箱,如果你需要創建包含 null 值的數組,Kotlin 也提供了 arrayOfNulls
方法,幫助我們進行創建。
val notActualPeople: Array<Person?> = arrayOfNulls<Person>(13)
Iterable 和 Sequence 的區別
Iterable
Iterable
對應 Java 的 java.lang.Iterable
, Iterable
會立即處理輸入的元素,並返回一個包含結果的新集合。我們來舉一個簡單的例子 返回年齡 > 21 前 5 個人的集合。
val people: List<Person> = getPeople()
val allowedEntrance = people
.filter { it.age >= 21 }
.map { it.name }
.take(5)
- 首先通過
filter
函數檢查每個人的年齡,將結果放入到一個新的結果集中 - 通過
map
函數對上一步得到的結果進行名字映射,然後生成一個新的列錶list<String>
- 通過
take
函數返回前 5 個元素,得到最終的結果集
Sequence
Sequence
是 Kotlin 中一個新的概念,用來錶示一個延遲計算的集合。Sequence
只存儲操作過程,並不處理任何元素,直到遇到終端操作符才開始處理元素,我們也可以通過 asSequence
擴展函數,將現有的集合轉換為 Sequence
,代碼如下所示。
val people: List<Person> = getPeople()
val allowedEntrance = people.asSequence()
.filter { it.age >= 21 }
.map { it.name }
.take(5)
.toList()
在這個例子中, toList()
錶示終端操作符,filter
、 map
、 take
都是中間操作符,返回 Sequence
實例,當 Sequence
遇到中間操作符時,只是存儲操作過程,並不參與計算,直到遇到 toList()
。
Sequence
的好處它不會生成中間結果集,直接對原始列錶中的每一個人重複這個步驟,直到找到 5 個人,返回最終的結果集。
應該如何選擇
如果數據量比較小,可以使用 Iterable。雖然會創建中間結果集,在數據不大的情况下,對性能的影響不會很嚴重。
如果處理的數據量比較大,Sequence 是最好的選擇,因為不會創建中間結果集,內存開銷更小。
常用的 8 種 For 循環遍曆方法
我們經常會使用以下方法進行遍曆。
for (i in 0..args.size - 1) {
println(args[i])
}
但是 Array
有一個可讀性更强的擴展屬性 lastIndex
。
for (i in 0..args.lastIndex) {
println(args[i])
}
但是實際上我們不需要知道最後一個索引,有一個更加簡單的寫法。
for (i in 0 until args.size) {
println(args[i])
}
當然你也可以使用下標擴展屬性 indices
得到它的範圍。
for (i in args.indices) {
println(args[i])
}
還有一個更加直接的寫法,通過下面的方式直接迭代集合。
for (arg in args) {
println(arg)
}
您也可以使用 forEach
函數,傳遞一個 lambda
錶達式來處理每個元素。
args.forEach { arg ->
println(arg)
}
它們生成的 Java 代碼都非常的相似,在這些例子中,都增加一個索引變量,並在循環中通過索引獲取元素。但是如果我們迭代的是 List
,最後兩個例子底層使用 Iterator,而其他的例子仍是通過索引獲取元素。另外還有兩個遍曆的方法:
withIndex
函數,它返回一個Iterable
對象,該對象可以被解構為當前索引和元素。
for ((index, arg) in args.withIndex()) {
println("$index: $arg")
}
forEachIndexed
函數,它為每個索引和參數提供了一個 lambda 錶達式。
args.forEachIndexed { index, arg ->
println("$index: $arg")
}
如何使用 SAM 轉換
可以通過 lambda 錶達式實現 SAM 轉換,從而使代碼更簡潔,可讀性更强,我們來看一個例子。
在 Java 中定義一個 OnClickListener
接口,並聲明一個 onClick
的方法。
public interface OnClickListener {
void onClick(Button button);
}
我們給 Button
添加 OnClickListener
監聽器,每次點擊的時候都會被調用。
public class Button {
public void setListener(OnClickListener listener) { ... }
}
在 Kotlin 中常見的寫法,創建匿名類,實現 OnClickListener
接口。
button.setListener(object: OnClickListener {
override fun onClick(button: Button) {
println("Clicked!")
}
})
如果我們使用 SAM 轉換,將使代碼更簡潔,可讀性更强。
button.setListener {
fun onClick(button: Button) {
println("Clicked!")
}
}
如何聲明一個靜態成員,Java 和 Koltin 進行互操作
一個普通的類可以有靜態成員和非靜態成員,在 Kotlin 中我們常把靜態成員放到 companion object
中。
class Foo {
companion object {
fun x() { ... }
}
fun y() { ... }
}
我們也可以用 object
來聲明一個單例,替換 companion object
。
object Foo {
fun x() { ... }
}
如果你不想總是通過類名去調用 Foo.x()
, 而只是想使用 x()
,可以聲明為頂級函數。
fun x() { ... }
另外想在 Java 中調用 Kotlin 中靜態方法,需要添加 @JvmStatic
和 @JvmName
注解。用一張錶格匯總一下,如何在 Java 中調用 Kotlin 中的代碼。
Function declaration | Kotlin usage | Java usage |
---|---|---|
Companion object | Foo. F () | Foo. Companion. F (); |
Companion object with @JvmStatic | Foo. F () | Foo. F (); |
Object | Foo. F () | Foo. INSTANCE. F (); |
Object with @JvmStatic | Foo. F () | Foo. F (); |
Top level function | f () | UtilKt. F (); |
Top level function with @JvmName | f () | Util. F (); |
同樣的規則也適用於變量,@JvmField
注解用於變量上,加上 const
關鍵字,編譯時可以將常量值內聯到調用處。
Variable declaration | Kotlin usage | Java usage |
---|---|---|
Companion object | X. X | X. Companion. GetX (); |
Companion object with @JvmStatic | X. X | X. GetX (); |
Companion object with @JvmField | X. X | X. X; |
Companion object with const | X. X | X. X; |
Object | X. X | X. INSTANCE. GetX (); |
Object with @JvmStatic | X. X | X. GetX (); |
Object with @JvmField | X. X | X. X |
Object with const | X. X | X. X; |
Top level variable | X. X | ConstKt. GetX (); |
Top level variable with @JvmField | X. X | ConstKt. X; |
Top level variable with const | x | ConstKt. X; |
Top level variable with @JvmName | x | Const. GetX (); |
Top level variable with @JvmName and @JvmField | x | Const. X; |
Top level variable with @JvmName and const | x | Const. X; |
為什麼 kotlin 中的智能轉換不能用於可變屬性
我們先來看一段有問題的代碼。
class Dog(var toy: Toy? = null) {
fun play() {
if (toy != null) {
toy.chew()
}
}
}
上面的代碼在編譯時無法通過,异常信息如下所示。
Kotlin: Smart cast to 'Toy' is impossible, because 'toy' is a mutable property that could have been changed by this time
出現這個問題的原因在於,執行完 toy != null
之後和 toy.chew()
方法被調用之間,這個 Dog
的實例可能被另外一個線程修改,這可能會出現 NullPointerException
异常。
如何才能解决這個問題呢
只需要將變量設置為不可變的,即用 val
聲明,那麼上面的問題就不存在,默認情况將所有的變量都用 val
聲明,除非有必要的時候,才將它們設置為 var
。
如果一定要聲明為 var
,那麼可以使用局部不可變的副本來解决這個問題。修改一下上面的代碼,如下所示。
class Dog(var toy: Toy? = null) {
fun play() {
val _toy = toy
if (_toy != null) {
_toy.chew()
}
}
}
但是還有一個更簡潔的寫法。
class Dog(var toy: Toy? = null) {
fun play() {
toy?.length
}
}
當重寫 Java 函數時,如何决定參數的可空性
在 Java 中定義一個 OnClickListener
接口,並聲明一個 onClick
的方法。
public interface OnClickListener {
void onClick(Button button);
}
在 Kotlin 中實現這個接口,並通過 IDEA
自動生成 onClick
方法,將會得到下面的方法簽名,onClick
方法參數默認為可空類型。
class KtListener: OnClickListener {
override fun onClick(button: Button?): Unit {
val name = button?.name ?: "Unknown button"
println("Clicked $name")
}
}
由於 Java 平臺沒有可空類型,而 Kotlin 中有,在這個例子中 Button 是否為空由我們來决定。默認情况下,對所有參數使用可空類型更安全,編譯器會强制我們處理這些參數。
對於已知的永遠不會空的參數,可以使用非空類型,空和非空都可以正常編譯,但是如果將方法參數聲明為非空,那麼 Kotlin 編譯器會自動注入一個空的檢查,可能會拋出 IllegalArgumentException
异常,潜在的風險很大。當然使用非空參數,代碼將會更加簡潔。
class KtListener: OnClickListener {
override fun onClick(button: Button): Unit {
val name = button.name
println("Clicked $name")
}
}
如何在一個文件中使用多個具有相同名稱的擴展函數和類
假設在不同的包中對 String 類實現了兩個相同名字的擴展函數,如果是一個普通函數,你可以使用完全限定包名來調用它,但是擴展函數不行。所以我們可以在 import
語句中使用 as
關鍵字對其重命名,代碼如下所示。
import com.example.code.indent as indent4
import com.example.square.indent as indent2
"hello world".indent4()
另外一個案例,想在同一個文件中使用來自不同包中兩個具有相同名稱的類(例如 java.util.Date
和 java.sql.Date
),並且您不希望通過完全限定包名來調用它們。我們也可以在 import
語句中使用 as
關鍵字對其重命名。
import java.util.Date as UtilDate
import java.sql.Date as SqlDate
現在我們就可以在這個類中,使用通過 as
關鍵字聲明的別名來引用這些類。
譯者
全文到這裏就結束了,這篇文章每個問題,都是一個知識點,後面我會針對每個問題,單獨寫一篇文章,進行更加深入的分析。
如果有幫助點個贊就是對我最大的鼓勵
代碼不止,文章不停
近期必讀熱門文章
版權聲明
本文為[hi-dhl]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/134/202205140557595478.html
邊欄推薦
猜你喜歡
隨機推薦
- 【服務器數據恢複】硬盤壞道和不穩定扇區導致服務器崩潰的數據恢複案例
- 性能測試報告編寫技巧
- ASP.NET對Cookie的操作方法有哪些
- SAP UI5 應用開發教程之八十七 - 如何讓 SAP UI5 Mock 服務器支持自定義 url 參數試讀版
- Redis基礎之溫故
- 神經網絡中的反向傳播&&參數更新
- 深度學習基礎知識點(一)CNN卷積神經網絡——1.卷積方面的原理
- 從PlatEMO中提取真實PF前沿
- G020-OP-INS-RHEL-02 RedHat OpenStack 發放雲主機(命令行)
- 解决報錯: AttributeError: module ‘distutils‘ has no attribute ‘version‘,親測有效
- 一文了解字節跳動如何解决 SLA 治理難題
- openstack底層提取所有虛擬機IP和其uuid、openstack底層提取所有虛擬機的所在宿主機
- 安裝mysql-community-server報錯缺少libaio依賴
- gin框架疑問, 為什麼不使用 *RouterGroup
- Redis分頁
- 【數據庫系統工程師】6.4數據倉庫和數據挖掘基礎知識
- PyTorch分類識別例子
- npx hardhat verify YOUR_CONTRACT_ADDRESS --network rinkeby
- 番外篇-穀粒商城的數據庫錶結構設計SQL語句
- 19. 删除鏈錶的倒數第 N 個結點
- c# 獲取枚舉描述的擴展方法
- 應如何認定解除合同通知的效力?
- 遊戲行業實戰案例5:玩家在線分布
- 【LeetCode】Day59-醜數 & 不同的二叉搜索樹
- CTFSHOW MISC入門
- 【國產免費】分布式作業批量處理平臺TASKCTL驗證的不同方式
- 數學建模學習(66):支持向量機 (SVM)案例實戰
- Thanos Sidecar組件
- Meta AI 宣布對人腦和語言處理進行長期研究
- 檢討書範文生成微信小程序工具源碼-支持流量主
- 元組類型(C# 參考)
- PTC:元宇宙引發醫療設備研發重大變革
- 面試題 01.05. 一次編輯 / 劍指 Offer II 041. 滑動窗口的平均值
- 華為機試第十一題:HJ11 數字顛倒
- Pascal VOC2012數據集
- unzip命令
- flink(scala版)學習一之常用的source
- [電路]7-實際電源模型和等效變換
- Go語言type自定義類型哦
- Pytorch和GPU有關操作(CUDA)