溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

為什么不要用Java的語法思維來寫Kotlin

發(fā)布時間:2021-12-01 11:24:21 來源:億速云 閱讀:120 作者:iii 欄目:編程語言

這篇文章主要講解了“為什么不要用Java的語法思維來寫Kotlin”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“為什么不要用Java的語法思維來寫Kotlin”吧!


1.盡可能的少用 !!

個人感覺對于Null的檢查是Koltin最語法糖的東西了,強制在編碼過程中考慮空指針,因此《十億美元的錯誤》,也許你不會再有這個機會犯錯了(也許可以說成,你賺了十億美金^_^)。

首先需要介紹是!!操作符。

!! 操作符:這是為空指針愛好者準備的,非空斷言運算符(!!)將任何值轉(zhuǎn)換為非空類型,若該值為空則拋出異常。我們可以寫 a!! ,這會返回一個非空的 a  值 (例如:在我們例子中的 String)或者如果 a 為空,就會拋出一個 空指針 異常:

val b = a!!.length

所以,我們能不用!!操作符就不要用。。。

下面介紹幾種方式避免使用!!操作符

1).多用 val 而不是 var

在 Kotlin 中 val代表只讀,var代表可變。建議盡可能多的使用val。val是線程安全的,并且必須在定義時初始化,所以不需要擔心 null  的問題。只需要注意 val 在某些情況下也是可變的就行了。

val 和 var 是用于表示屬性是否有 getter/setter:

  • var:同時有 getter 和 setter

  • val:只有 getter

所以,強烈推薦能用val的地方就用val。

2).使用 lateinit

有些時候并不能用val,比如在spring boot接口測試中就不能使用val,對于這種情況,可以使用 lateinit 關鍵字。。

依賴倒轉(zhuǎn),對象的創(chuàng)建是通過spring完成的,而val要求定義的時候初始化

/**  * Created by quanke on 2018/1/9.  * Site:http://woquanke.com .  */ @RunWith(SpringRunner::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class ApplicationTests {     val log = LogFactory.getLog(ApplicationTests::class.java)!!     @Autowired     lateinit var restTemplate: TestRestTemplate          @Test     fun `GET when given quanke then returns "Hello, quanke"`() {         // Given         val name = "quanke"         // When         val body = restTemplate.getForObject("/users/hello/{name}", String::class.java, name)         // Then         assertThat(body).isEqualTo("Hello, $name")     } }

注意:lateinit很好用,但也有坑

訪問未初始化的 lateinit 屬性會導致 UninitializedPropertyAccessException。

lateinit 不支持基礎數(shù)據(jù)類型,比如 Int。對于基礎數(shù)據(jù)類型,我們可以這樣:

> private var mNumber: Int by Delegates.notNull<Int>() >

3).Elvis 操作符

當b為可空引用時,我們可以使用if表達式處理

val l: Int = if (b != null) b.length else -1

但更加優(yōu)雅的方式是使用Elvis 操作符?:

val l = b?.length ?: -1

如果 ?: 左側(cè)表達式非空,elvis 操作符就返回其左側(cè)表達式,否則返回右側(cè)表達式。

  • 注意:當且僅當左側(cè)為空時,才會對右側(cè)表達式求值。

4).也許可以嘗試一下let函數(shù)

let函數(shù)一般與安全調(diào)用操作符一起使用,我們首先介紹安全調(diào)用操作?.

b?.length

如果 b 非空,就返回 b.length,否則返回 null,這個表達式的類型是 Int?。

安全調(diào)用在鏈式調(diào)用中很有用。例如,如果一個員工Quanke可能會(或者不會)分配給一個部門, 并且可能有另外一個員工是該部門的負責人,那么獲取  Quanke 所在部門負責人(如果有的話)的名字,我們寫作:

quanke?.department?.head?.name

如果任意一個屬性(環(huán)節(jié))為空,這個鏈式調(diào)用就會返回 null。

如果要只對非空值執(zhí)行某個操作,安全調(diào)用操作符可以與 let 一起使用:

val listWithNulls: List<String?> = listOf("A", null) for (item in listWithNulls) {      item?.let { println(it) } // 輸出 A 并忽略 null }

還有一種常見的錯誤(放ide里面試試就知道什么錯誤了):

private var a: String? = null fun aLetDemo() {     if (a != null) {         test(a)     } }

我們可以這樣:

private var a: String? = null fun aLetDemo() {     if (a != null) {         test(a!!)     } }

但是這樣的后果就是你還是需要在test函數(shù)里處理空指針。

我們充分利用?.加let的特點,更加優(yōu)雅的解決這個編譯錯誤,如下

private var a: String? = null fun aLetDemo() {     if (a != null) {         a?.let {             test(it)         }     } }

2.少寫點Util類和繼承

很多時候框架提供給我的方法是比較原子,或者某些常用的方法框架并沒有提供,Java一般是寫一個工具類:

public final class StringUtil {     /**      * 刪除所有的標點符號      *      * @param str 處理的字符串      */     public  static String trimPunct(String str) {         if(isEmpty(str)){             return "";         }         return str.replaceAll("[\\pP\\p{Punct}]", "");     } }

Kotlin可以通過擴展函數(shù)的形式實現(xiàn):

/**  * 刪除所有的標點符號  *  * @param str 處理的字符串  */ fun String.trimPunct(): String {     return if (this.isEmpty()) {         ""     } else this.replace("[\\pP\\p{Punct}]".toRegex(), "") }

調(diào)用:

fun main(args: Array<String>) {     val a = "把我的標點符號去掉吧,全科。"     print(a.trimPunct()) }

打?。?/p>

把我的標點符號去掉吧全科 Process finished with exit code 0

3.別再用+號拼接字符串

無論是Java還是Android開發(fā),我們都會用到字符串拼接,比如進行日志輸出等等。在Kotlin中,支持字符串模板,我們可以很輕松的完成一個字符串數(shù)的拼接,當然你可能會說使用StringBuilder性能更好,比如:

val site = "http://woquanke.com" val sb: StringBuilder = StringBuilder() sb.append("我的博客名字叫《我全科》,我的博客地址是:") sb.append(site) println(sb.toString())

但kotlin的字符串模版可以優(yōu)雅的做這個事情:

val site = "http://woquanke.com" println("我的博客名字叫《我全科》,我的博客地址是:$site")

4.也許可以忘記getters/setters了

我們經(jīng)常創(chuàng)建一些只保存數(shù)據(jù)的類。在這些類中,一些標準函數(shù)往往是操作一下ide生成的。在 Kotlin 中,這叫做 數(shù)據(jù)類 并標記為 data:

data class User(val name: String, val age: Int)

data class 自動生成getter,setting,hashcode和equals等方法

5.請忘記三元運算符

在 Kotlin 中,if是一個表達式,即它會返回一個值。因此就不需要三元運算符(條件 ? 然后 : 否則),因為普通的 if 就能勝任這個角色。

// 作為表達式 val max = if (a > b) a else b

6.哪里還有switch

when 取代了類java 語言的 switch 操作符。其最簡單的形式如下:

when (x) {     1 -> print("x == 1")     2 -> print("x == 2")     else -> { // 注意這個塊         print("x is neither 1 nor 2")     } }

如果很多分支需要用相同的方式處理,則可以把多個分支條件放在一起,用逗號分隔:

when (x) {     0, 1 -> print("x == 0 or x == 1")     else -> print("otherwise") }

可以用任意表達式(而不只是常量)作為分支條件

when (x) {     parseInt(s) -> print("s encodes x")     else -> print("s does not encode x") }

也可以檢測一個值在(in)或者不在(!in)一個區(qū)間或者集合中:

when (x) {     in 1..10 -> print("x is in the range")     in validNumbers -> print("x is valid")     !in 10..20 -> print("x is outside the range")     else -> print("none of the above") }

另一種可能性是檢測一個值是(is)或者不是(!is)一個特定類型的值。注意: 由于智能轉(zhuǎn)換,你可以訪問該類型的方法和屬性而無需任何額外的檢測。

fun hasPrefix(x: Any) = when(x) {     is String -> x.startsWith("prefix")     else -> false }

when 也可以用來取代 if-else if鏈。 如果不提供參數(shù),所有的分支條件都是簡單的布爾表達式,而當一個分支的條件為真時則執(zhí)行該分支:

when {     x.isOdd() -> print("x is odd")     x.isEven() -> print("x is even")     else -> print("x is funny") }

7.去你的ClassCastException

Kotlin智能轉(zhuǎn)換(Smart Casts)

對于不可變的值,Kotlin一般不需要顯式轉(zhuǎn)換對象類型,編譯器能跟蹤is檢查類型,在需要時會自動插入類型轉(zhuǎn)換代碼(安全):

fun classCast(a: Any) {     if (a is String) {         print(a.length) //編譯器自動把a轉(zhuǎn)換為String類型     } }

Kotlin編譯器很聰明,能識別反向檢查類型!is操作符,會自動插入類型轉(zhuǎn)換代碼:

if (a !is String) return     print(a.length) //編譯器自動把x轉(zhuǎn)換為String類型:     // ||右側(cè), a自動轉(zhuǎn)換為String類型     if (a !is String || a.length == 0) return     // &&右側(cè), a自動轉(zhuǎn)換為String類型     if (a is String && a.length > 0) {         print(a.length) // a 自動轉(zhuǎn)換為字符串     }     //智能轉(zhuǎn)換(smart casts)也用于when表達式和while循環(huán)     when (a) {         is Int -> print(a + 1)         is String -> print(a.length + 1)         is IntArray -> print(a.sum())     }

如果不能保證變量在類型檢查is/!is操作符和變量使用之間不可改變時,智能轉(zhuǎn)換不能用。智能轉(zhuǎn)換的適用條件或規(guī)則:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)

  2. val局部變量-總是適用!

  3. val屬性-適用于private或internal,或者類型檢查is/!is在聲明屬性的同一模塊中執(zhí)行;

  4. 不適用于open的屬性,或者具有自定義getter的屬性!

  5. var局部變量&mdash;適用于變量在類型檢查和使用之間沒有修改,且不在修改它的lambda中捕獲!

  6. var屬性-不適用(因為該變量可隨時被修改)

安全(可空)轉(zhuǎn)換-操作符as?

為避免拋出異常,可用安全轉(zhuǎn)換操作符as?,在失敗時返回null

val a: String? = b as? String

盡管as?右邊是一個非空類型String,但是as?轉(zhuǎn)換失敗時返回可空(null),換句話說就是,as?函數(shù)參數(shù)String不能為null,但是as?函數(shù)的返回值可以是null

8.真的要習慣Koltin的for循環(huán),太強大了

Kotlin沒有Java中的for(初始值;條件;增減步長)這個規(guī)則。但是Kotlin中對于for循環(huán)語句新增了其他的規(guī)則,來滿足剛提到的規(guī)則。

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)

  2. for循環(huán)提供迭代器用來遍歷任何東西

  3. for循環(huán)數(shù)組被編譯為一個基于索引的循環(huán),它不會創(chuàng)建一個迭代器對象

新增的規(guī)則,去滿足for(初始值;條件;增減步長)這個規(guī)則

遞增

關鍵字:until

范圍:until[n,m) => 即大于等于n,小于m

例:

// 循環(huán)5次,且步長為1的遞增 for (i in 0 until 5){   print("i => $i \t") }

輸出結(jié)果為

i => 0 i => 1 i => 2 i => 3 i => 4

遞減

關鍵字:downTo

范圍:downTo[n,m] => 即小于等于n,大于等于m ,n > m

例:

// 循環(huán)5次,且步長為1的遞減 for (i in 15 downTo 11){     print("i => $i \t") }

輸出結(jié)果為:

i => 15 i => 14 i => 13 i => 12 i => 11

符號(&rsquo; .. &lsquo;) 表示遞增的循環(huán)的另外一種操作

使用符號( &lsquo;..&rsquo;).

范圍:..[n,m]=> 即大于等于n,小于等于m

和until的區(qū)別,一是簡便性。二是范圍的不同。

例:

print("使用 符號`..`的打印結(jié)果\n") for (i in 20 .. 25){     print("i => $i \t") } println() print("使用until的打印結(jié)果\n") for (i in 20 until 25){     print("i => $i \t") }

輸出結(jié)果為:

使用 符號..的打印結(jié)果

i => 20 i => 21 i => 22 i => 23 i => 24 i => 25

使用until的打印結(jié)果

i => 20 i => 21 i => 22 i => 23 i => 24

設置步長

關鍵字:step

例:

for (i in 10 until 16 step 2){     print("i => $i \t") }

輸出結(jié)果為:

i => 10 i => 12 i => 14

迭代

  • for循環(huán)提供一個迭代器用來遍歷任何東西。

  • for循環(huán)數(shù)組被編譯為一個基于索引的循環(huán),它不會創(chuàng)建一個迭代器對象

遍歷字符串

此用法在數(shù)據(jù)類型章節(jié)中的字符串類型中用到過。還不甚清楚的可以查看 Kotlin&mdash;&mdash;最詳細的數(shù)據(jù)類型介紹。

例:

for (i in "abcdefg"){     print("i => $i \t") }

輸出結(jié)果為:

i => a i => b i => c i => d i => e i => f i => g

遍歷數(shù)組

此用法在數(shù)據(jù)類型章節(jié)中的數(shù)組類型中用到過。還不甚清楚的可以查看 Kotlin&mdash;&mdash;最詳細的數(shù)據(jù)類型介紹。

例:

var arrayListOne = arrayOf(10,20,30,40,50) for (i in arrayListOne){     print("i => $i \t") }

輸出結(jié)果為:

i => 10 i => 20 i => 30 i => 40 i => 50

使用數(shù)組的indices屬性遍歷

例:

var arrayListTwo = arrayOf(1,3,5,7,9) for (i in arrayListTwo.indices){     println("arrayListTwo[$i] => " + arrayListTwo[i]) }

輸出結(jié)果為:

arrayListTwo[0] => 1 arrayListTwo[1] => 3 arrayListTwo[2] => 5 arrayListTwo[3] => 7 arrayListTwo[4] => 9

使用數(shù)組的withIndex()方法遍歷

例:

var arrayListTwo = arrayOf(1,3,5,7,9) for ((index,value) in arrayListTwo.withIndex()){     println("index => $index \t value => $value") }

輸出結(jié)果為:

index => 0   value => 1 index => 1   value => 3 index => 2   value => 5 index => 3   value => 7 index => 4   value => 9

使用列表或數(shù)組的擴展函數(shù)遍歷

數(shù)組或列表有一個成員或擴展函數(shù)iterator()實現(xiàn)了Iterator接口,且該接口提供了next()與hasNext()兩個成員或擴展函數(shù)

其一般和while循環(huán)一起使用

可以查看Array.kt這個類??梢钥匆娖渲械膇terator()函數(shù),而這個函數(shù)實現(xiàn)了Iterator接口。

/**   *   Creates an iterator for iterating over the elements of the array.   */ public operator fun iterator(): Iterator<T>

查看Iterator.kt這個接口類,這個接口提供了hasNext()函數(shù)和next()函數(shù)。

public interface Iterator<out T> { /**   * Returns the next element in the iteration.   */ public operator fun next(): T /**   * Returns `true` if the iteration has more elements.   */ public operator fun hasNext(): Boolean }

例:

var arrayListThree = arrayOf(2,'a',3,false,9) var iterator: Iterator<Any> = arrayListThree.iterator() while (iterator.hasNext()){     println(iterator.next()) }

輸出結(jié)果為:

2 a 3 false 9

9.kotlin stream 真心可以

流式處理給我們的集合操作帶來了很大的方便,其實Java 8 一樣支持流式處理,我只是想在這里推廣一下 stream。

下面舉例:

val names = arrayOf("Amy", "Alex", "Bob", "Cindy", "Jeff", "Jack", "Sunny", "Sara", "Steven")      //篩選S開頭的人名   val sName = names.filter { it.startsWith("S") }.toList()      //按首字母分組并排序   val nameGroup = names.groupBy { it[0] }           .toSortedMap( Comparator { key1, key2 -> key1.compareTo(key2) })

關于更多流式處理,請自行搜索Java stream

10.少寫點方法重載

因為kotlin支持默認參數(shù),所以在封裝方法時會少很多的方法重載的。

如果沒有默認參數(shù)的需要實現(xiàn)下面的日志打印,需要寫多個方法:

fun log(tag: String, content: String) {     println("tag:$tag-->$content") } fun log( content: String) {     log("quanke","") }

使用默認參數(shù)只需要一個方法:

fun log(tag: String="quanke", content: String) {     println("tag:$tag-->$content") }

感謝各位的閱讀,以上就是“為什么不要用Java的語法思維來寫Kotlin”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對為什么不要用Java的語法思維來寫Kotlin這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI