真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎樣避免Kotlin里的陷阱

今天給大家介紹一下怎樣避免Kotlin里的陷阱。文章的內(nèi)容小編覺得不錯(cuò),現(xiàn)在給大家分享一下,覺得有需要的朋友可以了解一下,希望對(duì)大家有所幫助,下面跟著小編的思路一起來閱讀吧。

成都創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比東昌府網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式東昌府網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋東昌府地區(qū)。費(fèi)用合理售后完善,十余年實(shí)體公司更值得信賴。

最近 Kotlin 特別流行,并且我也贊同 Kotlin  是一個(gè)經(jīng)過深思熟慮后被設(shè)計(jì)出的語言,除了下面提到的缺點(diǎn)之外。下面向你分析一些我在開發(fā)過程中遇到的陷阱,并且教你如何避免他們。

謎一樣的 null

在 Kotlin 當(dāng)中,你可以不用考慮在你的代碼中如何處理 null 的問題,這會(huì)讓你忘記 null  是無處不在的這個(gè)說法,只不過被隱藏了起來??纯聪旅孢@個(gè)表面看起來沒有問題的類:

class Foo {     private val c: String     init {         bar()         c = ""     }     private fun bar() {         println(c.length)     } }

如果你嘗試初始化這個(gè)類,那么代碼就會(huì)拋出一個(gè) NullPointerException。因?yàn)?bar 方法嘗試在 c 變量初始化之前就訪問它。

盡管這個(gè)代碼本身就是有問題的,才導(dǎo)致異常拋出。但是更糟糕的是你的編譯器不會(huì)發(fā)現(xiàn)這一點(diǎn)。

Kotlin 可以幫你在絕大部分情況下避免 null,但是你不能因此而忘記 null 的存在。否則遲早有一天你會(huì)碰上類似的問題。

來自 JDK 的 null

Kotlin 的標(biāo)準(zhǔn)庫能夠很好地處理 null。但是如果你使用了 JDK 中的類,你需要自己處理關(guān)于 JDK 方法調(diào)用可能產(chǎn)生的空指針。

大部分情況下 Kotlin 的標(biāo)準(zhǔn)庫就足夠了,但是有時(shí)你需要使用到 ConcurrentHashMap:

val map = ConcurrentHashMap() map["foo"] = "bar" val bar: String = map["foo"]!!

這時(shí),你需要使用 !! 操作符。但某些情況下你還可以使用像 (?) 這樣的對(duì) null 安全的操作符來替換它。盡管如此,當(dāng)你使用 !! 或者 ?  ,或者編寫了一個(gè)適配器來使用 Java 類庫的時(shí)候,你會(huì)發(fā)現(xiàn)代碼因?yàn)檫@些修改而變的混亂。這是你無法避免的問題。

你還可能會(huì)碰上更多更可怕的問題。當(dāng)你使用 JDK 類中的方法的時(shí)候,返回值可能是null,而且沒有什么像 Map 訪問一樣的語法糖。

考慮如下例子:

val queue: Queue = LinkedList() queue.peek().toInt()

這種情況下,你使用了可能返回 null 值的 peek 方法。但是 Kotlin 編譯器不會(huì)提示你這個(gè)問題,所以當(dāng)你的 Queue  是空隊(duì)列的的時(shí)候,可能會(huì)觸發(fā) NullPointerException 異常。

問題在于我們使用的 Queue 是 JDK 的一個(gè)接口,并且當(dāng)你查看 peek 方法的文檔時(shí):

/**   * Retrieves, but does not remove, the head of this queue,   * or returns {@code null} if this queue is empty.   *   * @return the head of this queue, or {@code null} if this queue is empty   */   E peek();

文檔中說 peek 方法會(huì)返回一個(gè) E 類型的對(duì)象,但是 Kotlin 認(rèn)為 E 是不可空的。在接下來的 Kotlin  版本中可能會(huì)解決這個(gè)問題,但是現(xiàn)在當(dāng)你在你的工程中使用類似接口的時(shí)候,一定要注意:

val queue: Queue = LinkedList() queue.peek()?.toInt()

內(nèi)部 it

當(dāng)一個(gè) lambda 表達(dá)式只有一個(gè)參數(shù)的時(shí)候,你可以在你的代碼中將其省略,并用 it 代替。

  • it:單參數(shù)的內(nèi)部名稱。當(dāng)你表達(dá)式只有一個(gè)參數(shù)的時(shí)候,這是一個(gè)很有用的特性,聲明的過程可以省略(就像 ->),并且參數(shù)名稱為 it。

問題是,當(dāng)你的代碼中存在向下面例子一樣的嵌套函數(shù)的時(shí)候:

val list = listOf("foo.bar", "baz.qux") list.forEach {     it.split(".").forEach {         println(it)     } }

it 參數(shù)會(huì)混淆。解決方法就是像下面這樣顯示的聲明:

list.forEach { item ->     item.split(".").forEach { part ->         println(part)     } }

看起來是不是好多了!

隱藏的復(fù)制

注意觀察下面的類:

data class Foo(val bars: MutableList)

data 類提供了一系列的方法,并且你可以通過拷貝得到其鏡像。猜猜下面的代碼會(huì)輸出什么?

val bars = mutableListOf("foobar", "wombar") val foo0 = Foo(bars) val foo1 = foo0.copy() bars.add("oops") println(foo1.bars.joinToString())

控制臺(tái)會(huì)輸出 foobar, wombar, oops。問題出在 copy  方法并沒有真正地復(fù)制一個(gè)完整的對(duì)象, 而是復(fù)制了對(duì)象的引用。當(dāng)你忘記編寫單元測試類,并且將你的 data 類按照不可變類來傳遞的時(shí)候,就可能出現(xiàn)這種問題。

解決方法就是當(dāng)你使用 data 類的時(shí)候一定要多加小心,并且當(dāng)你必須將其作為值對(duì)象的時(shí)候,像下面這樣:

data class Foo(val bars: List)
  • data 類還有一個(gè)問題:其 equals / hashCode  方法所用到的屬性不可變。你只能通過手工重寫這些方法的方式來修改返回值。謹(jǐn)記上面這一點(diǎn)。

內(nèi)部方法暴露

仔細(xì)思考下面的例子:

class MyApi {     fun operation0() {     }     internal fun hiddenOperation() {                 } }

當(dāng)你在 Kotlin 的項(xiàng)目中引用這個(gè)類的時(shí)候,internal 關(guān)鍵字是生效的。但是當(dāng)你從一個(gè) Java  項(xiàng)目中使用的時(shí)候,hiddenOperation 就變成了一個(gè)公共方法!為了避免這種情況,我建議使用接口的方式來隱藏實(shí)現(xiàn)的細(xì)節(jié):

interface MyApi {     fun operation0() } class MyApiImpl: MyApi {     override fun operation0() {     }     internal fun hiddenOperation() {     } }

特殊的全局?jǐn)U展

毫無疑問,擴(kuò)展函數(shù)的功能非常重要。但通常,能力越大責(zé)任越大。例如,你可以編寫全局的 JDK  類擴(kuò)展函數(shù)。但是當(dāng)這個(gè)函數(shù)只在本地上下文中有意義,卻是全局可見的時(shí)候,就會(huì)帶來很多麻煩。

fun String.extractCustomerName() : String {     // ... }

每個(gè)跳轉(zhuǎn)到你的方法的人都會(huì)不知所措。所以我認(rèn)為在你編寫這樣的方法之前務(wù)必三思。下面就是一個(gè)建議:

/**  * Returns an element of this [List] wrapped in an Optional  * which is empty if `idx` is out of bounds.  */ fun  List.getIfPresent(idx: Int) =         if (idx >= size) {             Optional.empty()         } else {             Optional.of(get(idx))         } /**  * Negates `isPresent`.  */ fun  Optional.isNotPresent() = isPresent.not()

lambdas Unit 返回值 vs Java SAM 轉(zhuǎn)換

如果你的函數(shù)參數(shù)是 lambdas 表達(dá)式,并且返回值類型是 Unit 的時(shí)候,你可以省略return 關(guān)鍵字:

fun consumeText(text: String, fn: (String) -> Unit) { } // usage consumeText("foo") {     println(it) }

這是一個(gè)很有趣的特性,但是當(dāng)你在 Java 代碼中調(diào)用該方法的時(shí)候會(huì)比較尷尬:

consumeText("foo", (text) -> {     System.out.println(text);     return Unit.INSTANCE; });

這對(duì)于 Java 端來說是不友好的,如果你想在 Java 中成功調(diào)用該方法,你需要定義如下接口:

nterface StringConsumer {     fun consume(text: String) } fun consumeText(text: String, fn: StringConsumer) { }

然后你就能使用 Java 的 SAM 轉(zhuǎn)換。

consumeText("foo", System.out::println);

但是在 Kotlin 這邊看起來就很糟糕了:

consumeText("foo", object: StringConsumer {     override fun consume(text: String) {         println(text)     } })

問題關(guān)鍵點(diǎn)在于只有 Java 支持 SAM 轉(zhuǎn)換,Kotlin 并不支持。我的建議是簡單的場景中,只是用 Java 的 SAM  接口作為一個(gè)消費(fèi)者:

fun consumeText(text: String, fn: Consumer) { } // usage from Kotlin consumeText("foo", Consumer {     println(it) }) // usage from Java consumeText("foo", System.out::println);

Java 中使用不可變集合

Kotlin 提供了 JDK 集合類的不可變版本。

fun createSet(): Set = setOf("foo") // ... createSet().add("bar") // oops, compile error

這是一個(gè)很好的補(bǔ)充。但是當(dāng)你在看 Java JDK 的 Set 類 API 的時(shí)候會(huì)發(fā)現(xiàn):

createSet().add("bar"); // UnsupportedOperationException

當(dāng)你嘗試修改這個(gè) Set 的時(shí)候,就會(huì)拋出這個(gè)異常,就像你使用了Collections.unmodifiableSet()  方法一樣。我不知道這種情況是否合理,但是你在使用 Kotlin 不可變版本的 Java 集合類的時(shí)候,需要謹(jǐn)記這一點(diǎn)。

接口中沒有重載

Kotlin 在接口上不支持使用 @JvmOverloads 注解,當(dāng)然 override 也不行。

interface Foo {     @JvmOverloads // OUCH!     fun bar(qux: String) } class FooImpl : Foo {      @JvmOverloads // YIKES!     override fun bar(qux: String) {     } }

你只能像下面這樣手動(dòng)定義:

interface Foo {     fun bar()     fun bar(qux: String) }

要記住你可以使用 Kotlin 中的 KEEP (Kotlin Evolution and Enhancement Process) 來改善。KEEP 與  Java 中的 JEP 類似,但是與 JEP 相比要簡潔許多。

Kotlin 現(xiàn)下很流行,并且我也認(rèn)為他是一個(gè)增強(qiáng)版的 Java。但是在使用 Kotlin 的時(shí)候你仍需要保持清醒,尤其是當(dāng)你身處各種各樣的關(guān)于  Kotlin 的宣傳之中時(shí)。如果你要使用 Kotlin 的話,一定要注意我們在上面提到的 Kotlin 相關(guān)的缺陷。

以上就是怎樣避免Kotlin里的陷阱的全部內(nèi)容了,更多與怎樣避免Kotlin里的陷阱相關(guān)的內(nèi)容可以搜索創(chuàng)新互聯(lián)之前的文章或者瀏覽下面的文章進(jìn)行學(xué)習(xí)哈!相信小編會(huì)給大家增添更多知識(shí),希望大家能夠支持一下創(chuàng)新互聯(lián)!


網(wǎng)站題目:怎樣避免Kotlin里的陷阱
網(wǎng)站地址:http://weahome.cn/article/pghhis.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部