<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          拋棄 Java 改用 Kotlin 的六個月后,我后悔了!

          共 11982字,需瀏覽 24分鐘

           ·

          2020-09-20 19:13

          原文:

          https://allegro.tech/2018/05/From-Java-to-Kotlin-and-Back-Again.html

          https://zsmb.co/on-from-java-to-kotlin-and-back-again/

          譯者:安翔,責(zé)編:郭芮

          毫無疑問,Kotlin 目前很受歡迎,業(yè)界甚至有人認為其將取代 Java 的霸主地位。它提供了 Null 安全性,從這一點來說它確實比 Java 更好。那么是不是這就意味著開發(fā)者應(yīng)該毫不猶豫地擁抱 Kotlin,否則就落伍了?

          等等,或許事情并非如此。

          在開始使用 Kotlin 編程之前,本文想要分享個故事給你。在這個故事中,作者最早使用 Kotlin 來編寫一個項目,后來 Kotlin 的各種怪異模式以及一些其他障礙越來越讓人厭煩,最終,他們決定重寫這個項目。

          以下為譯文:

          一直以來,我對基于 JVM 的語言都非常情有獨鐘。我通常會用 Java 來編寫主程序,再用 Groovy 編寫測試代碼,兩者配合使用得心應(yīng)手。

          2017年夏天,團隊發(fā)起了一個新的微服務(wù)項目,和往常一樣,我們需要對編程語言和技術(shù)進行選型。部分團隊成員是 Kotlin 的擁護者,再加上我們都想嘗試一下新的東西,于是我們決定用 Kotlin 來開發(fā)這個項目。由于 Spock 測試框架不支持 Kotlin,因此我們決定堅持使用 Groovy 來測試。

          2018年春天,使用 Kotlin 開發(fā)幾個月之后,我們總結(jié)了 Kotlin 的優(yōu)缺點,最終結(jié)論表明 Kotlin 降低了我們的生產(chǎn)力。

          于是我們使用 Java 來重寫這個微服務(wù)項目。

          那么 Kotlin 主要存在哪些弊端?下面來一一解釋。

          名稱遮蔽

          這是 Kotlin 最讓我震驚的地方。看看下面這個方法:

          fun?inc(num?:?Int)?{
          ????val?num?=?2
          ????if?(num?>?0)?{
          ????????val?num?=?3
          ????}
          ????println?("num:?"?+?num)
          }

          當(dāng)你調(diào)用 inc(1) 會輸出什么呢?在 Kotlin 中, 方法的參數(shù)無法修改,因此在本例中你不能改變 num。這個設(shè)計很好,因為你不應(yīng)該改變方法的輸入?yún)?shù)。但是你可以用相同的名稱定義另一個變量并對其進行初始化。

          這樣一來,這個方法作用域中就有兩個名為 num 的變量。當(dāng)然,你一次只能訪問其中一個 num,但是 num 值會被改變。

          在 if 語句中再添加另一個 num,因為作用域的原因 num 并不會被修改。

          于是,在 Kotlin 中,inc(1) 會輸出 2。同樣效果的 Java 代碼如下所示,不過無法通過編譯:?

          void?inc(int?num)?{
          ????int?num?=?2;?//error:?variable?'num'?is?already?defined?in?the?scope
          ????if?(num?>?0)?{
          ????????int?num?=?3;?//error:?variable?'num'?is?already?defined?in?the?scope
          ????}
          ????System.out.println?("num:?"?+?num);
          }

          名字遮蔽并不是 Kotlin 發(fā)明的,這在編程語言中很常見。在 Java 中我們習(xí)慣用方法參數(shù)來映射類字段:

          public?class?Shadow?{
          ????int?val;
          ????public?Shadow(int?val)?{
          ????????this.val?=?val;
          ????}
          }

          在 Kotlin 中名稱遮蔽有些嚴重,這是 Kotlin 團隊的一個設(shè)計缺陷。

          IDEA 團隊試圖通過向每個遮蔽變量顯示警告信息來解決這個問題。兩個團隊在同一家公司工作,或許他們可以互相交流并就遮蔽問題達成共識。我從個人角度贊成 IDEA 的做法因為我想不到有哪些應(yīng)用場景需要遮蔽方法參數(shù)。

          類型推斷

          在Kotlin中,當(dāng)你聲明一個var或是val,你通常會讓編譯器從右邊的表達式類型中猜測變量類型。我們稱之為局部變量類型推斷,這對程序員來說是一個很大的改進。它允許我們在不影響靜態(tài)類型檢查的情況下簡化代碼。

          例如,這個Kotlin代碼:

          var?a?=?"10"

          Kotlin 編譯器會將其翻譯成:?

          var?a?:?String?=?"10"

          Java 同樣具備這個特性,Java 10中的類型推斷示例如下:??

          var?a?=?"10";

          實話實說,Kotlin 在這一點上確實更勝一籌。當(dāng)然,類型推斷還可應(yīng)用在多個場景。關(guān)于 Java 10中的局部變量類型推斷,點擊以下鏈接了解更多:

          • https://medium.com/@afinlay/java-10-sneak-peek-local-variable-type-inference-var-3022016e1a2b


          Null 安全類型

          Null 安全類型是 Kotlin 的殺手級功能。

          這個想法很好,在 Kotlin 中,類型默認不可為空。如果你需要添加一個可為空的類型,可以像下列代碼這樣:?

          val?a:?String??=?null??????//?ok
          val?b:?String?=?null???????//?compilation?error

          假設(shè)你使用了可為空的變量但是并未進行空值檢查,這在 Kotlin 將無法通過編譯,比如:

          println?(a.length)??????????//?compilation?error
          println?(a?.length)?????????//?fine,?prints?null
          println?(a?.length??:?0)????//?fine,?prints?0

          那么是不是如果你同時擁有不可為空和可為空的變量,就可以避免 Java 中最常見的 NullPointerException 異常嗎?事實并沒有想象的簡單。

          當(dāng) Kotlin 代碼必須調(diào)用 Java 代碼時,事情會變得很糟糕,比如庫是用 Java 編寫的,我相信這種情況很常見。于是第三種類型產(chǎn)生了,它被稱為平臺類型。Kotlin 無法表示這種奇怪的類型,它只能從 Java 類型推斷出來。它可能會誤導(dǎo)你,因為它對空值很寬松,并且會禁用 Kotlin 的 NULL 安全機制。

          看看下面這個 Java 方法:

          public?class?Utils?{
          ????static?String?format(String?text)?{
          ????????return?text.isEmpty()???null?:?text;
          ????}
          }

          假如你想調(diào)用 format(String)。應(yīng)該使用哪種類型來獲得這個 Java 方法的結(jié)果呢?你有三個選擇。

          第一種方法:你可以使用 String,代碼看起來很安全,但是會拋出 NullPointerException 異常。

          fun?doSth(text:?String)?{
          ????val?f:?String?=?Utils.format(text)???????//?compiles?but?assignment?can?throw?NPE?at?runtime
          ????println?("f.len?:?"?+?f.length)
          }

          那你就需要用 Elvis 來解決這個問題:

          fun?doSth(text:?String)?{
          ????val?f:?String?=?Utils.format(text)??:?""??//?safe?with?Elvis
          ????println?("f.len?:?"?+?f.length)
          }

          第二種方法:你可以使用 String,能夠保證 Null 安全性。

          fun?doSth(text:?String)?{
          ????val?f:?String??=?Utils.format(text)???//?safe
          ????println?("f.len?:?"?+?f.length)???????//?compilation?error,?fine
          ????println?("f.len?:?"?+?f?.length)??????//?null-safe?with???operator
          }

          第三種方法:讓 Kotlin 做局部變量類型推斷如何??

          fun?doSth(text:?String)?{
          ????val?f?=?Utils.format(text)????????????//?f?type?inferred?as?String!
          ????println?("f.len?:?"?+?f.length)???????//?compiles?but?can?throw?NPE?at?runtime
          }

          餿主意!這個 Kotlin 代碼看起來很安全、可編譯,但是它容忍了空值,就像在 Java 中一樣。

          除此之外,還有另外一個方法,就是強制將 f 類型推斷為 String:

          fun?doSth(text:?String)?{
          ????val?f?=?Utils.format(text)!!??????????//?throws?NPE?when?format()?returns?null
          ????println?("f.len?:?"?+?f.length)
          }

          在我看來,Kotlin 的所有這些類似 scala 的類型系統(tǒng)過于復(fù)雜。Java 互操作性似乎損害了 Kotlin 類型推斷這個重量級功能。

          類名稱字面常量

          使用類似 Log4j 或者 Gson 的 Java 庫時,類文字很常見。

          Java 使用 .class 后綴編寫類名:?

          Gson?gson?=?new?GsonBuilder().registerTypeAdapter(LocalDate.class,?new?LocalDateAdapter()).create();

          Groovy 把類進行了進一步的簡化。你可以忽略 .class,它是 Groovy 或者 Java 類并不重要。

          def?gson?=?new?GsonBuilder().registerTypeAdapter(LocalDate,?new?LocalDateAdapter()).create()

          Kotlin 把 Kotlin 類和 Java 類進行了區(qū)分,并為其提供了語法規(guī)范:

          val?kotlinClass?:?KClass?=?LocalDate::class
          val?javaClass?:?Class?=?LocalDate::class.java

          因此在 Kotlin 中,你必須寫成如下形式:

          val?gson?=?GsonBuilder().registerTypeAdapter(LocalDate::class.java,?LocalDateAdapter()).create()

          這看起來非常丑陋。

          反向類型聲明

          C 系列的編程語言有標(biāo)準(zhǔn)的聲明類型的方法。簡而言之,首先指定一個類型,然后是該符合類型的東西,比如變量、字段、方法等等。

          Java 中的表示方法是:

          int?inc(int?i)?{
          ????return?i?+?1;
          }

          Kotlin 中則是:

          fun?inc(i:?Int):?Int?{
          ????return?i?+?1
          }

          這種方法有幾個原因令人討厭。

          首先,你需要在名稱和類型之間加入這個多余的冒號。這個額外角色的目的是什么?為什么名稱與其類型要分離?我不知道。可悲的是,這讓你在 Kotlin 的工作變得更加困難。

          第二個問題,當(dāng)你讀取一個方法聲明時,你首先看到的是名字和返回類型,然后才是參數(shù)。

          在 Kotlin 中,方法的返回類型可能遠在行尾,所以需要瀏覽很多代碼才能看到:?

          private?fun?getMetricValue(kafkaTemplate?:?KafkaTemplate<String,?ByteArray>,?metricName?:?String)?:?Double?{
          ????...
          }

          或者,如果參數(shù)是逐行格式的,則需要搜索。那么我們需要多少時間才能找到此方法的返回類型呢?

          @Bean
          fun?kafkaTemplate(
          ????????@Value("\${interactions.kafka.bootstrap-servers-dc1}")?bootstrapServersDc1:?String,
          ????????@Value("\${interactions.kafka.bootstrap-servers-dc2}")?bootstrapServersDc2:?String,
          ????????cloudMetadata:?CloudMetadata,
          ????????@Value("\${interactions.kafka.batch-size}")?batchSize:?Int,
          ????????@Value("\${interactions.kafka.linger-ms}")?lingerMs:?Int,
          ????????metricRegistry?:?MetricRegistry
          )
          :?KafkaTemplate?{
          ????val?bootstrapServer?=?if?(cloudMetadata.datacenter?==?"dc1")?{
          ????????bootstrapServersDc1
          ????}
          ????...
          }

          第三個問題是 IDE 中的自動化支持不夠好。標(biāo)準(zhǔn)做法從類型名稱開始,并且很容易找到類型。一旦選擇一個類型,IDE 會提供一些關(guān)于變量名的建議,這些變量名是從選定的類型派生的,因此你可以快速輸入這樣的變量:?

          MongoExperimentsRepository?repository

          Kotlin 盡管有 IntelliJ 這樣強大的 IDE,輸入變量仍然是很難的。如果你有多個存儲庫,在列表中很難實現(xiàn)正確的自動補全,這意味著你不得不手動輸入完整的變量名稱。

          repository?:?MongoExperimentsRepository

          伴生對象

          一位 Java 程序員來到 Kotlin 面前。

          “嗨,Kotlin。我是新來的,我可以使用靜態(tài)成員嗎?"他問。

          ?“不行。我是面向?qū)ο蟮模o態(tài)成員不是面向?qū)ο蟮摹!?Kotlin 回答。

          ?“好吧,但我需要 MyClass 的 logger,我該怎么辦?”?

          “這個沒問題,使用伴生對象即可。”

          ?“那是什么東西?” “這是局限到你的類的單獨對象。把你的 logger 放在伴生對象中。”Kotlin解釋說。

          ?“我懂了。這樣對嗎?”

          class?MyClass?{
          ????companion?object?{
          ????????val?logger?=?LoggerFactory.getLogger(MyClass::class.java)
          ????}
          }

          “正確!”

          ?“很詳細的語法,”程序員看起來很疑惑,“但是沒關(guān)系,現(xiàn)在我可以像 MyClass.logger 這樣調(diào)用我的 logger,就像 Java 中的一個靜態(tài)成員?”?

          “嗯......是的,但它不是靜態(tài)成員!這里只有對象。把它看作是已經(jīng)實例化為單例的匿名內(nèi)部類。事實上,這個類并不是匿名的,它的名字是 Companion,但你可以省略這個名字。看到了嗎?這很簡單。"

          我很欣賞對象聲明的概念——單例很有用。但從語言中刪除靜態(tài)成員是不切實際的。在 Java 中我們使用靜態(tài) Logger 很經(jīng)典,它只是一個 Logger,所以我們不關(guān)心面向?qū)ο蟮募兌取K軌蚬ぷ鳎瑥膩頉]有任何壞處。

          因為有時候你必須使用靜態(tài)。舊版本 public static void main() 仍然是啟動 Java 應(yīng)用程序的唯一方式。

          class?AppRunner?{
          ????companion?object?{
          ????????@JvmStatic?fun?main(args:?Array<String>)?{
          ????????????SpringApplication.run(AppRunner::class.java,?*args)
          ????????}
          ????}
          }

          集合字面量

          在Java中,初始化列表非常繁瑣:

          import?java.util.Arrays;
          ...
          List<String>?strings?=?Arrays.asList("Saab",?"Volvo");

          初始化地圖非常冗長,很多人使用 Guava:

          import?com.google.common.collect.ImmutableMap;
          ...
          Map<String,?String>?string?=?ImmutableMap.of("firstName",?"John",?"lastName",?"Doe");

          在 Java 中,我們?nèi)匀辉诘却碌恼Z法來表達集合和映射。語法在許多語言中非常自然和方便。

          JavaScript:

          const?list?=?['Saab',?'Volvo']
          const?map?=?{'firstName':?'John',?'lastName'?:?'Doe'}

          Python:

          list?=?['Saab',?'Volvo']
          map?=?{'firstName':?'John',?'lastName':?'Doe'}

          Groovy:

          def?list?=?['Saab',?'Volvo']
          def?map?=?['firstName':?'John',?'lastName':?'Doe']

          簡單來說,集合字面量的整齊語法就是你對現(xiàn)代編程語言的期望,特別是如果它是從頭開始創(chuàng)建的。Kotlin 提供了一系列內(nèi)置函數(shù),比如 listOf()、mutableListOf()、mapOf()、hashMapOf() 等等。

          Kotlin:?

          val?list?=?listOf("Saab",?"Volvo")
          val?map?=?mapOf("firstName"?to?"John",?"lastName"?to?"Doe")

          在地圖中,鍵和值與 to 運算符配對,這很好。但為什么一直沒有得到廣泛使用呢?令人失望。

          Maybe

          函數(shù)式語言(比如 Haskell)沒有空值。相反,他們提供 Maybe monad(如果你不熟悉monad,請閱讀 Tomasz Nurkiewicz 的這篇文章:http://www.nurkiewicz.com/2016/06/functor-and-monad-examples-in-plain-java.html)。

          Maybe 很久以前就被 Scala 以 Option 引入到 JVM 世界,然后在 Java 8 中被采用為 Optional。如今,Optional 是在 API 邊界處理返回類型中的空值的非常流行的方式。

          Kotlin 中沒有 Optional 的等價物,所以你大概應(yīng)該使用 Kotlin 的可空類型。讓我們來調(diào)查一下這個問題。

          通常情況下,當(dāng)你有一個 Optional 的時候,你想要應(yīng)用一系列無效的轉(zhuǎn)換。

          例如,在 Java 中:?

          public?int?parseAndInc(String?number)?{
          ????return?Optional.ofNullable(number)
          ???????????????????.map(Integer::parseInt)
          ???????????????????.map(it?->?it?+?1)
          ???????????????????.orElse(0);
          }

          在 Kotlin 中,為了映射你可以使用 let 函數(shù):

          fun?parseAndInc(number:?String?):?Int?{
          ????return?number.let?{?Integer.parseInt(it)?}
          ?????????????????.let?{?it?->?it?+?1?}??:?0
          }

          上面的代碼是錯誤的,parseInt() 會拋出 NPE 。map() 僅在有值時執(zhí)行。否則,Null 就會跳過,這就是為什么 map() 如此方便。不幸的是,Kotlin 的 let 不會那樣工作。它從左側(cè)的所有內(nèi)容中調(diào)用,包括空值。

          為了保證這個代碼 Null 安全,你必須在每個代碼之前添加 let:?

          fun?parseAndInc(number:?String?):?Int?{
          ????return?number?.let?{?Integer.parseInt(it)?}
          ??????????????????.let?{?it?->?it?+?1?}??:?0
          }

          現(xiàn)在,比較 Java 和 Kotlin 版本的可讀性。你更傾向哪個?

          數(shù)據(jù)類

          數(shù)據(jù)類是 Kotlin 在實現(xiàn) Value Objects 時使用的方法,以減少 Java 中不可避免的樣板問題。

          例如,在 Kotlin 中,你只寫一個 Value Object :

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

          Kotlin 對 equals()、hashCode()、toString() 以及 copy() 有很好的實現(xiàn)。在實現(xiàn)簡單的DTO 時它非常有用。但請記住,數(shù)據(jù)類帶有嚴重的局限性。你無法擴展數(shù)據(jù)類或者將其抽象化,所以你可能不會在核心模型中使用它們。

          這個限制不是 Kotlin 的錯。在 equals() 沒有違反 Liskov 原則的情況下,沒有辦法產(chǎn)生正確的基于價值的數(shù)據(jù)。

          這也是為什么 Kotlin 不允許數(shù)據(jù)類繼承的原因。

          開放類

          Kotlin 類默認為 final。如果你想擴展一個類,必須添加 open 修飾符。

          繼承語法如下所示:?

          open?class?Base
          class?Derived?:?Base()

          Kotlin 將 extends 關(guān)鍵字更改為: 運算符,該運算符用于將變量名稱與其類型分開。那么再回到 C ++語法?對我來說這很混亂。

          這里有爭議的是,默認情況下類是 final。也許 Java 程序員過度使用繼承,也許應(yīng)該在考慮擴展類之前考慮三次。但我們生活在框架世界,Spring 使用 cglib、jassist 庫為你的 bean 生成動態(tài)代理。Hibernate 擴展你的實體以啟用延遲加載。

          如果你使用 Spring,你有兩種選擇。你可以在所有 bean 類的前面添加 open,或者使用這個編譯器插件:?

          buildscript?{
          ????dependencies?{
          ????????classpath?group:?'org.jetbrains.kotlin',?name:?'kotlin-allopen',?version:?"$versions.kotlin"
          ????}
          }

          陡峭的學(xué)習(xí)曲線

          如果你認為自己有 Java 基礎(chǔ)就可以快速學(xué)習(xí) Kotlin,那你就錯了。Kotlin 會讓你陷入深淵,事實上,Kotlin 的語法更接近 Scala。這是一項賭注,你將不得不忘記 Java 并切換到完全不同的語言。

          相反,學(xué)習(xí) Groovy 是一個愉快的過程。Java 代碼是正確的 Groovy 代碼,因此你可以通過將文件擴展名從 .java 更改為 .groovy。

          最后的想法

          學(xué)習(xí)新技術(shù)就像一項投資。我們投入時間,新技術(shù)讓我們得到回報。但我并不是說 Kotlin 是一種糟糕的語言,只是在我們的案例中,成本遠超收益。

          以上內(nèi)容編譯自 From Java to Kotlin and Back Again,作者 Kotlin ketckup。

          他是一名具有15年以上專業(yè)經(jīng)驗的軟件工程師,專注于JVM 。在 Allegro,他是一名開發(fā)團隊負責(zé)人,JaVers 項目負責(zé)人,Spock 倡導(dǎo)者。此外,他還是 allegro.tech/blog 的主編。

          本文一出就引發(fā)了業(yè)內(nèi)的廣泛爭議,Kotlin 語言擁護者 Márton Braun 就表示了強烈的反對。

          Márton Braun 十分喜歡 Kotlin 編程,目前他在 StackOverflow 上 Kotlin 標(biāo)簽的最高用戶列表中排名第三,并且是兩個開源 Kotlin 庫的創(chuàng)建者,最著名的是 MaterialDrawerKt。此外他還是 Autosoft 的 Android 開發(fā)人員,目前正在布達佩斯技術(shù)經(jīng)濟大學(xué)攻讀計算機工程碩士學(xué)位。

          以下就是他針對上文的反駁:

          當(dāng)我第一次看到這篇文章時,我就想把它轉(zhuǎn)發(fā)出來看看大家會怎么想,我肯定它會是一個有爭議的話題。后來我讀了這篇文章,果然證明了它是一種主觀的、不真實的、甚至有些居高臨下的偏見。

          有些人已經(jīng)在原貼下進行了合理的批評,對此我也想表達一下自己的看法。

          名稱遮蔽

          “IDEA 團隊”(或者 Kotlin 插件團隊)和“Kotlin 團隊”肯定是同樣的人,我從不認為內(nèi)部沖突會是個好事。語言提供這個功能給你,你需要的話就使用,如果討厭,調(diào)整檢查設(shè)置就是了。

          類型推斷

          Kotlin 的類型推斷無處不在,作者說的 Java 10 同樣可以簡直是在開玩笑。

          Kotlin 的方式超越了推斷局部變量類型或返回表達式體的函數(shù)類型。這里介紹的這兩個例子是那些剛剛看過關(guān)于 Kotlin 的第一次介紹性講話的人會提到的,而不是那些花了半年學(xué)習(xí)該語言的人。

          例如,你怎么能不提 Kotlin 推斷泛型類型參數(shù)的方式?這不是 Kotlin 的一次性功能,它深深融入了整個語言。

          編譯時 Null 安全

          這個批評是對的,當(dāng)你與 Java 代碼進行互操作時,Null 安全性確實被破壞了。該語言背后的團隊曾多次聲明,他們最初試圖使 Java 可為空的每種類型,但他們發(fā)現(xiàn)它實際上讓代碼變得更糟糕。

          Kotlin 不比 Java 更差,你只需要注意使用給定庫的方式,就像在 Java 中使用它一樣,因為它并沒有不去考慮 Null 安全。如果 Java 庫關(guān)心 Null 安全性,則它們會有許多支持注釋可供添加。

          也許可以添加一個編譯器標(biāo)志,使每種 Java 類型都可以為空,但這對 Kotlin 團隊來說不得不花費大量額外資源。

          類名稱字面常量

          :: class 為你提供了一個 KClass 實例,以便與 Kotlin 自己的反射 API 一起使用,而:: class.java為你提供了用于 Java 反射的常規(guī) Java 類實例。

          反向類型聲明

          為了清楚起見,顛倒的順序是存在的,這樣你就可以以合理的方式省略顯式類型。冒號只是語法,這在現(xiàn)代語言中是相當(dāng)普遍的一種,比如 Scala、Swift 等。

          我不知道作者在使用什么 IntelliJ,但我使用的變量名稱和類型都能夠自動補全。對于參數(shù),IntelliJ 甚至?xí)o你提供相同類型的名稱和類型的建議,這實際上比 Java 更好。

          伴生對象

          原文中說:

          有時候你必須使用靜態(tài)。舊版本 public static void main() 仍然是啟動 Java 應(yīng)用程序的唯一方式。

          class?AppRunner?{
          ????companion?object?{
          ????????@JvmStatic?fun?main(args:?Array<String>)?{
          ????????????SpringApplication.run(AppRunner::class.java,?*args)
          ????????}
          ????}
          }

          實際上,這不是啟動 Java 應(yīng)用程序的唯一方式。你可以這樣做:

          ?fun?main(args:Array ){?SpringApplication.run(AppRunner?::?class.java,*?args)}?

          或者這樣:

          ?fun?main(args:Array ){?runApplication?(*?args)}

          集合字面量

          你可以在注釋中使用數(shù)組文字。但是,除此之外,這些集合工廠的功能非常簡潔,而且它們是另一種“內(nèi)置”到該語言的東西,而它們實際上只是庫函數(shù)。

          你只是抱怨使用:進行類型聲明。而且,為了獲得它不必是單獨的語言結(jié)構(gòu)的好處,它只是一個任何人都可以實現(xiàn)的功能。

          Maybe

          如果你喜歡 Optional ,你可以使用它。Kotlin 在 JVM 上運行。

          對于代碼確實這有些難看。但是你不應(yīng)該在 Kotlin 代碼中使用 parseInt,而應(yīng)該這樣做(我不知道你使用該語言的 6 個月中為何錯過這個)。你為什么要明確地命名一個 Lambda 參數(shù)呢?

          數(shù)據(jù)類

          原文中說:

          這個限制不是 Kotlin 的錯。在 equals() 沒有違反 Liskov 原則的情況下,沒有辦法產(chǎn)生正確的基于價值的數(shù)據(jù)。

          這就是為什么 Kotlin 不允許數(shù)據(jù)類繼承的原因。

          我不知道你為什么提出這個問題。如果你需要更復(fù)雜的類,你仍然可以創(chuàng)建它們并手動維護它們的 equals、hashCode 等方法。數(shù)據(jù)類僅僅是一個簡單用例的便捷方式,對于很多人來說這很常見。

          公開類

          作者再次鄙視了,對此我實在無話可說。

          陡峭的學(xué)習(xí)曲線

          作者認為學(xué)習(xí) Kotlin 很難, 但是我個人并不這么認為。

          最后的想法

          從作者列舉的例子中,我感覺他只是了解語言的表面。

          很難想象他對此有投入很多時間。



          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本一区二区黄色 | 日韩欧美内射 | 怡红院成人av | 亚洲va国产 | 免费黄色毛片 |