自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

不要用Java的語(yǔ)法思維來(lái)寫Kotlin

開發(fā) 后端
如果你是像我一樣是一名優(yōu)秀的Java開發(fā)者^(guò)_^,而且已經(jīng)想用kotlin來(lái)實(shí)現(xiàn)你的程序,那么,抱歉!不要用Java的語(yǔ)法思維來(lái)寫Kotlin,不要讓kotlin的優(yōu)雅埋沒。如果你沒有Java開發(fā)經(jīng)驗(yàn),下面的內(nèi)容也對(duì)你會(huì)有幫助。。
[[222108]]

寫了多年的Java,直到看到Kotlin,原來(lái)代碼可以如此優(yōu)雅!

如果你是像我一樣是一名優(yōu)秀的Java開發(fā)者^(guò)_^,而且已經(jīng)想用kotlin來(lái)實(shí)現(xiàn)你的程序,那么,抱歉!不要用Java的語(yǔ)法思維來(lái)寫Kotlin,不要讓kotlin的優(yōu)雅埋沒。如果你沒有Java開發(fā)經(jīng)驗(yàn),下面的內(nèi)容也對(duì)你會(huì)有幫助。。。

1.盡可能的少用 !!

個(gè)人感覺對(duì)于Null的檢查是Koltin最語(yǔ)法糖的東西了,強(qiáng)制在編碼過(guò)程中考慮空指針,因此《十億美元的錯(cuò)誤》,也許你不會(huì)再有這個(gè)機(jī)會(huì)犯錯(cuò)了(也許可以說(shuō)成,你賺了十億美金^_^)。

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

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

  1. val b = a!!.length 

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

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

1).多用 val 而不是 var

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

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

  • var:同時(shí)有 getter 和 setter
  • val:只有 getter

所以,強(qiáng)烈推薦能用val的地方就用val。

2).使用 lateinit

有些時(shí)候并不能用val,比如在spring boot接口測(cè)試中就不能使用val,對(duì)于這種情況,可以使用 lateinit 關(guān)鍵字。。

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

 

  1. /** 
  2.  * Created by quanke on 2018/1/9. 
  3.  * Site:http://woquanke.com . 
  4.  */ 
  5. @RunWith(SpringRunner::class) 
  6. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
  7. class ApplicationTests { 
  8.     val log = LogFactory.getLog(ApplicationTests::class.java)!! 
  9.     @Autowired 
  10.     lateinit var restTemplate: TestRestTemplate 
  11.      
  12.     @Test 
  13.     fun `GET when given quanke then returns "Hello, quanke"`() { 
  14.         // Given 
  15.         val name = "quanke" 
  16.         // When 
  17.         val body = restTemplate.getForObject("/users/hello/{name}", String::class.java, name
  18.         // Then 
  19.         assertThat(body).isEqualTo("Hello, $name"
  20.     } 

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

訪問(wèn)未初始化的 lateinit 屬性會(huì)導(dǎo)致 UninitializedPropertyAccessException。

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

 

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

3).Elvis 操作符

當(dāng)b為可空引用時(shí),我們可以使用if表達(dá)式處理

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

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

  1. val l = b?.length ?: -1 

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

  • 注意:當(dāng)且僅當(dāng)左側(cè)為空時(shí),才會(huì)對(duì)右側(cè)表達(dá)式求值。

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

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

  1. b?.length 

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

安全調(diào)用在鏈?zhǔn)秸{(diào)用中很有用。例如,如果一個(gè)員工Quanke可能會(huì)(或者不會(huì))分配給一個(gè)部門, 并且可能有另外一個(gè)員工是該部門的負(fù)責(zé)人,那么獲取 Quanke 所在部門負(fù)責(zé)人(如果有的話)的名字,我們寫作:

  1. quanke?.department?.head?.name 

如果任意一個(gè)屬性(環(huán)節(jié))為空,這個(gè)鏈?zhǔn)秸{(diào)用就會(huì)返回 null。

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

 

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

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

 

  1. private var a: String? = null 
  2. fun aLetDemo() { 
  3.     if (a != null) { 
  4.         test(a) 
  5.     } 

我們可以這樣:

 

  1. private var a: String? = null 
  2. fun aLetDemo() { 
  3.     if (a != null) { 
  4.         test(a!!) 
  5.     } 

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

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

 

  1. private var a: String? = null 
  2. fun aLetDemo() { 
  3.     if (a != null) { 
  4.         a?.let { 
  5.             test(it) 
  6.         } 
  7.     } 

2.少寫點(diǎn)Util類和繼承

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

 

  1. public final class StringUtil { 
  2.     /** 
  3.      * 刪除所有的標(biāo)點(diǎn)符號(hào) 
  4.      * 
  5.      * @param str 處理的字符串 
  6.      */ 
  7.     public  static String trimPunct(String str) { 
  8.         if(isEmpty(str)){ 
  9.             return ""
  10.         } 
  11.         return str.replaceAll("[\\pP\\p{Punct}]"""); 
  12.     } 

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

 

  1. /** 
  2.  * 刪除所有的標(biāo)點(diǎn)符號(hào) 
  3.  * 
  4.  * @param str 處理的字符串 
  5.  */ 
  6. fun String.trimPunct(): String { 
  7.     return if (this.isEmpty()) { 
  8.         "" 
  9.     } else this.replace("[\\pP\\p{Punct}]".toRegex(), ""

調(diào)用:

 

  1. fun main(args: Array<String>) { 
  2.     val a = "把我的標(biāo)點(diǎn)符號(hào)去掉吧,全科。" 
  3.     print(a.trimPunct()) 

打?。?/p>

 

  1. 把我的標(biāo)點(diǎn)符號(hào)去掉吧全科 
  2. Process finished with exit code 0 

3.別再用+號(hào)拼接字符串

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

 

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

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

 

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

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

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

  1. data class User(val name: String, val age: Int

data class 自動(dòng)生成getter,setting,hashcode和equals等方法

5.請(qǐng)忘記三元運(yùn)算符

在 Kotlin 中,if是一個(gè)表達(dá)式,即它會(huì)返回一個(gè)值。因此就不需要三元運(yùn)算符(條件 ? 然后 : 否則),因?yàn)槠胀ǖ?if 就能勝任這個(gè)角色。

 

  1. // 作為表達(dá)式 
  2. val max = if (a > b) a else b 

6.哪里還有switch

when 取代了類java 語(yǔ)言的 switch 操作符。其最簡(jiǎn)單的形式如下:

 

  1. when (x) { 
  2.     1 -> print("x == 1"
  3.     2 -> print("x == 2"
  4.     else -> { // 注意這個(gè)塊 
  5.         print("x is neither 1 nor 2"
  6.     } 

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

 

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

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

 

  1. when (x) { 
  2.     parseInt(s) -> print("s encodes x"
  3.     else -> print("s does not encode x"

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

 

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

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

 

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

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

 

  1. when { 
  2.     x.isOdd() -> print("x is odd"
  3.     x.isEven() -> print("x is even"
  4.     else -> print("x is funny"

7.去你的ClassCastException

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

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

 

  1. fun classCast(a: Any) { 
  2.     if (a is String) { 
  3.         print(a.length) //編譯器自動(dòng)把a(bǔ)轉(zhuǎn)換為String類型 
  4.     } 

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

 

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

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

  1. val局部變量-總是適用!
  2. val屬性-適用于private或internal,或者類型檢查is/!is在聲明屬性的同一模塊中執(zhí)行;
  3. 不適用于open的屬性,或者具有自定義getter的屬性!
  4. var局部變量—適用于變量在類型檢查和使用之間沒有修改,且不在修改它的lambda中捕獲!
  5. var屬性-不適用(因?yàn)樵撟兞靠呻S時(shí)被修改)

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

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

  1. val a: String? = b as? String 

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

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

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

  1. for循環(huán)提供迭代器用來(lái)遍歷任何東西
  2. for循環(huán)數(shù)組被編譯為一個(gè)基于索引的循環(huán),它不會(huì)創(chuàng)建一個(gè)迭代器對(duì)象

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

遞增

關(guān)鍵字:until

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

例:

 

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

輸出結(jié)果為

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

遞減

關(guān)鍵字:downTo

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

例:

 

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

輸出結(jié)果為:

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

符號(hào)(’ .. ‘) 表示遞增的循環(huán)的另外一種操作

使用符號(hào)( ‘..’).

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

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

例:

 

  1. print("使用 符號(hào)`..`的打印結(jié)果\n"
  2. for (i in 20 .. 25){ 
  3.     print("i => $i \t"
  4. println() 
  5. print("使用until的打印結(jié)果\n"
  6. for (i in 20 until 25){ 
  7.     print("i => $i \t"

輸出結(jié)果為:

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

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

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

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

設(shè)置步長(zhǎng)

關(guān)鍵字:step

例:

 

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

輸出結(jié)果為:

  1. i => 10 i => 12 i => 14 

迭代

  • for循環(huán)提供一個(gè)迭代器用來(lái)遍歷任何東西。
  • for循環(huán)數(shù)組被編譯為一個(gè)基于索引的循環(huán),它不會(huì)創(chuàng)建一個(gè)迭代器對(duì)象

遍歷字符串

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

例:

 

  1. for (i in "abcdefg"){ 
  2.     print("i => $i \t"

輸出結(jié)果為:

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

遍歷數(shù)組

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

例:

 

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

輸出結(jié)果為:

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

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

例:

 

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

輸出結(jié)果為:

 

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

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

例:

 

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

輸出結(jié)果為:

 

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

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

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

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

可以查看Array.kt這個(gè)類。可以看見其中的iterator()函數(shù),而這個(gè)函數(shù)實(shí)現(xiàn)了Iterator接口。

 

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

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

 

  1. public interface Iterator<out T> { 
  2. /** 
  3.   * Returns the next element in the iteration. 
  4.   */ 
  5. public operator fun next(): T 
  6. /** 
  7.   * Returns `true` if the iteration has more elements. 
  8.   */ 
  9. public operator fun hasNext(): Boolean 

例:

 

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

輸出結(jié)果為:

 

  1. false 

9.kotlin stream 真心可以

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

下面舉例:

 

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

關(guān)于更多流式處理,請(qǐng)自行搜索Java stream

10.少寫點(diǎn)方法重載

因?yàn)閗otlin支持默認(rèn)參數(shù),所以在封裝方法時(shí)會(huì)少很多的方法重載的。

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

 

  1. fun log(tag: String, content: String) { 
  2.     println("tag:$tag-->$content"
  3. fun log( content: String) { 
  4.     log("quanke",""

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

 

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

***我還是想說(shuō):抱歉!不要用Java的語(yǔ)法思維來(lái)寫Kotlin!

責(zé)任編輯:未麗燕 來(lái)源: 程序師
相關(guān)推薦

2016-10-21 10:00:01

HTML標(biāo)簽WEB

2023-02-26 15:49:08

元宇宙ChatGPT

2013-02-28 09:42:25

DIND 10C++Python

2015-08-04 08:56:14

swift子類

2021-01-20 07:28:02

nullcollections對(duì)象

2023-04-27 13:25:22

索引合并MySQL

2022-04-13 08:43:46

工廠模式接口解析器

2018-03-15 09:23:24

編程語(yǔ)言程序員Java

2015-03-19 14:53:17

面向?qū)ο?/a>程序員新手程序員

2013-08-16 11:26:24

程序員面向?qū)ο?/a>

2015-01-09 15:22:11

2019-10-12 09:30:48

微信外掛

2022-05-31 12:26:50

移動(dòng)響應(yīng)css

2017-10-25 12:04:55

2018-06-05 10:30:28

KotlinJava語(yǔ)言

2009-06-15 10:21:07

基于JBossMBean

2018-11-29 11:18:11

VLANVPC數(shù)據(jù)中心

2018-11-01 18:02:30

百度智能城市ACE王牌計(jì)劃

2021-11-01 07:00:32

IP字符串數(shù)據(jù)

2020-04-21 09:20:43

JavaGo語(yǔ)言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)