點(diǎn)擊上方“Java技術(shù)江湖”,選擇“設(shè)為星標(biāo)”
回復(fù)”666“獲取全網(wǎng)最熱的Java核心知識(shí)點(diǎn)整理

作者:Bartosz Walacik | 來(lái)自:CSDN | 編輯 :可可原文:https://allegro.tech/2018/05/From-Java-to-Kotlin-and-Back-Again.html
毫無(wú)疑問(wèn),Kotlin 在去年很受歡迎,業(yè)界甚至有人認(rèn)為其將取代 Java 的霸主地位。它提供了 Null 安全性,從這一點(diǎn)來(lái)說(shuō)它確實(shí)比 Java 更好。那么是不是這就意味著開發(fā)者應(yīng)該毫不猶豫地?fù)肀?Kotlin,否則就落伍了?在開始使用 Kotlin 編程之前,本文想要分享個(gè)故事給你。在這個(gè)故事中,作者最早使用 Kotlin 來(lái)編寫一個(gè)項(xiàng)目,后來(lái) Kotlin 的各種怪異模式以及一些其他障礙越來(lái)越讓人厭煩,最終,他們決定重寫這個(gè)項(xiàng)目。
一直以來(lái),我對(duì)基于 JVM 的語(yǔ)言都非常情有獨(dú)鐘。我通常會(huì)用 Java 來(lái)編寫主程序,再用 Groovy 編寫測(cè)試代碼,兩者配合使用得心應(yīng)手。
2017年夏天,團(tuán)隊(duì)發(fā)起了一個(gè)新的微服務(wù)項(xiàng)目,和往常一樣,我們需要對(duì)編程語(yǔ)言和技術(shù)進(jìn)行選型。部分團(tuán)隊(duì)成員是 Kotlin 的擁護(hù)者,再加上我們都想嘗試一下新的東西,于是我們決定用 Kotlin 來(lái)開發(fā)這個(gè)項(xiàng)目。由于 Spock 測(cè)試框架不支持 Kotlin,因此我們決定堅(jiān)持使用 Groovy 來(lái)測(cè)試。2018年春天,使用 Kotlin 開發(fā)幾個(gè)月之后,我們總結(jié)了 Kotlin 的優(yōu)缺點(diǎn),最終結(jié)論表明 Kotlin 降低了我們的生產(chǎn)力。于是我們使用 Java 來(lái)重寫這個(gè)微服務(wù)項(xiàng)目。那么 Kotlin 主要存在哪些弊端?下面來(lái)一一解釋。這是 Kotlin 最讓我震驚的地方??纯聪旅孢@個(gè)方法:
fun inc(num : Int) {
val num = 2
if (num > 0) {
val num = 3
}
println ("num: " + num)
}
當(dāng)你調(diào)用 inc(1) 會(huì)輸出什么呢?在 Kotlin 中, 方法的參數(shù)無(wú)法修改,因此在本例中你不能改變 num。這個(gè)設(shè)計(jì)很好,因?yàn)槟悴粦?yīng)該改變方法的輸入?yún)?shù)。但是你可以用相同的名稱定義另一個(gè)變量并對(duì)其進(jìn)行初始化。這樣一來(lái),這個(gè)方法作用域中就有兩個(gè)名為 num 的變量。當(dāng)然,你一次只能訪問(wèn)其中一個(gè) num,但是 num 值會(huì)被改變。在 if 語(yǔ)句中再添加另一個(gè) num,因?yàn)樽饔糜虻脑?num 并不會(huì)被修改。于是,在 Kotlin 中,inc(1) 會(huì)輸出 2。同樣效果的 Java 代碼如下所示,不過(guò)無(wú)法通過(guò)編譯:
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ā)明的,這在編程語(yǔ)言中很常見。在 Java 中我們習(xí)慣用方法參數(shù)來(lái)映射類字段:
public class Shadow {
int val;
public Shadow(int val) {
this.val = val;
}
}
在 Kotlin 中名稱遮蔽有些嚴(yán)重,這是 Kotlin 團(tuán)隊(duì)的一個(gè)設(shè)計(jì)缺陷。IDEA 團(tuán)隊(duì)試圖通過(guò)向每個(gè)遮蔽變量顯示警告信息來(lái)解決這個(gè)問(wèn)題。兩個(gè)團(tuán)隊(duì)在同一家公司工作,或許他們可以互相交流并就遮蔽問(wèn)題達(dá)成共識(shí)。我從個(gè)人角度贊成 IDEA 的做法因?yàn)槲蚁氩坏接心男?yīng)用場(chǎng)景需要遮蔽方法參數(shù)。在Kotlin中,當(dāng)你聲明一個(gè)var或是val,你通常會(huì)讓編譯器從右邊的表達(dá)式類型中猜測(cè)變量類型。我們稱之為局部變量類型推斷,這對(duì)程序員來(lái)說(shuō)是一個(gè)很大的改進(jìn)。它允許我們?cè)诓挥绊戩o態(tài)類型檢查的情況下簡(jiǎn)化代碼。
Java 同樣具備這個(gè)特性,Java 10中的類型推斷示例如下:
實(shí)話實(shí)說(shuō),Kotlin 在這一點(diǎn)上確實(shí)更勝一籌。當(dāng)然,類型推斷還可應(yīng)用在多個(gè)場(chǎng)景。關(guān)于 Java 10中的局部變量類型推斷,點(diǎn)擊以下鏈接了解更多:Null 安全類型是 Kotlin 的殺手級(jí)功能。這個(gè)想法很好,在 Kotlin 中,類型默認(rèn)不可為空。如果你需要添加一個(gè)可為空的類型,可以像下列代碼這樣:
val a: String? = null // ok
val b: String = null // compilation error
假設(shè)你使用了可為空的變量但是并未進(jìn)行空值檢查,這在 Kotlin 將無(wú)法通過(guò)編譯,比如:
println (a.length) // compilation error
println (a?.length) // fine, prints null
println (a?.length ?: 0) // fine, prints 0
那么是不是如果你同時(shí)擁有不可為空和可為空的變量,就可以避免 Java 中最常見的 NullPointerException 異常嗎?事實(shí)并沒(méi)有想象的簡(jiǎn)單。當(dāng) Kotlin 代碼必須調(diào)用 Java 代碼時(shí),事情會(huì)變得很糟糕,比如庫(kù)是用 Java 編寫的,我相信這種情況很常見。于是第三種類型產(chǎn)生了,它被稱為平臺(tái)類型。Kotlin 無(wú)法表示這種奇怪的類型,它只能從 Java 類型推斷出來(lái)。它可能會(huì)誤導(dǎo)你,因?yàn)樗鼘?duì)空值很寬松,并且會(huì)禁用 Kotlin 的 NULL 安全機(jī)制。
public class Utils {
static String format(String text) {
return text.isEmpty() ? null : text;
}
}
假如你想調(diào)用 format(String)。應(yīng)該使用哪種類型來(lái)獲得這個(gè) Java 方法的結(jié)果呢?你有三個(gè)選擇。第一種方法:你可以使用 String,代碼看起來(lái)很安全,但是會(huì)拋出 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 來(lái)解決這個(gè)問(wèn)題:
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
}
餿主意!這個(gè) Kotlin 代碼看起來(lái)很安全、可編譯,但是它容忍了空值,就像在 Java 中一樣。除此之外,還有另外一個(gè)方法,就是強(qiáng)制將 f 類型推斷為 String:
fun doSth(text: String) {
val f = Utils.format(text)!! // throws NPE when format() returns null
println ("f.len : " + f.length)
}
在我看來(lái),Kotlin 的所有這些類似 scala 的類型系統(tǒng)過(guò)于復(fù)雜。Java 互操作性似乎損害了 Kotlin 類型推斷這個(gè)重量級(jí)功能。使用類似 Log4j 或者 Gson 的 Java 庫(kù)時(shí),類文字很常見。
Gson gson = new GsonBuilder().registerTypeAdapter(LocalDate.class, new LocalDateAdapter()).create();
Groovy 把類進(jìn)行了進(jìn)一步的簡(jiǎn)化。你可以忽略 .class,它是 Groovy 或者 Java 類并不重要。
def gson = new GsonBuilder().registerTypeAdapter(LocalDate, new LocalDateAdapter()).create()
Kotlin 把 Kotlin 類和 Java 類進(jìn)行了區(qū)分,并為其提供了語(yǔ)法規(guī)范:
val kotlinClass : KClass<LocalDate> = LocalDate::class
val javaClass : Class<LocalDate> = LocalDate::class.java
val gson = GsonBuilder().registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()).create()
C 系列的編程語(yǔ)言有標(biāo)準(zhǔn)的聲明類型的方法。簡(jiǎn)而言之,首先指定一個(gè)類型,然后是該符合類型的東西,比如變量、字段、方法等等。
int inc(int i) {
return i + 1;
}
fun inc(i: Int): Int {
return i + 1
}
首先,你需要在名稱和類型之間加入這個(gè)多余的冒號(hào)。這個(gè)額外角色的目的是什么?為什么名稱與其類型要分離?我不知道??杀氖牵@讓你在 Kotlin 的工作變得更加困難。第二個(gè)問(wèn)題,當(dāng)你讀取一個(gè)方法聲明時(shí),你首先看到的是名字和返回類型,然后才是參數(shù)。在 Kotlin 中,方法的返回類型可能遠(yuǎn)在行尾,所以需要瀏覽很多代碼才能看到:
private fun getMetricValue(kafkaTemplate : KafkaTemplate<String, ByteArray>, metricName : String) : Double {
...
}
或者,如果參數(shù)是逐行格式的,則需要搜索。那么我們需要多少時(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<String, ByteArray> {
val bootstrapServer = if (cloudMetadata.datacenter == "dc1") {
bootstrapServersDc1
}
...
}
第三個(gè)問(wèn)題是 IDE 中的自動(dòng)化支持不夠好。標(biāo)準(zhǔn)做法從類型名稱開始,并且很容易找到類型。一旦選擇一個(gè)類型,IDE 會(huì)提供一些關(guān)于變量名的建議,這些變量名是從選定的類型派生的,因此你可以快速輸入這樣的變量:
MongoExperimentsRepository repository
Kotlin 盡管有 IntelliJ 這樣強(qiáng)大的 IDE,輸入變量仍然是很難的。如果你有多個(gè)存儲(chǔ)庫(kù),在列表中很難實(shí)現(xiàn)正確的自動(dòng)補(bǔ)全,這意味著你不得不手動(dòng)輸入完整的變量名稱。
repository : MongoExperimentsRepository
一位 Java 程序員來(lái)到 Kotlin 面前。“嗨,Kotlin。我是新來(lái)的,我可以使用靜態(tài)成員嗎?"他問(wèn)。 “不行。我是面向?qū)ο蟮?,靜態(tài)成員不是面向?qū)ο蟮?。?Kotlin 回答。 “好吧,但我需要 MyClass 的 logger,我該怎么辦?” “這個(gè)沒(méi)問(wèn)題,使用伴生對(duì)象即可?!?/span> “那是什么東西?” “這是局限到你的類的單獨(dú)對(duì)象。把你的 logger 放在伴生對(duì)象中?!盞otlin解釋說(shuō)。
class MyClass {
companion object {
val logger = LoggerFactory.getLogger(MyClass::class.java)
}
}
“很詳細(xì)的語(yǔ)法,”程序員看起來(lái)很疑惑,“但是沒(méi)關(guān)系,現(xiàn)在我可以像 MyClass.logger 這樣調(diào)用我的 logger,就像 Java 中的一個(gè)靜態(tài)成員?” “嗯......是的,但它不是靜態(tài)成員!這里只有對(duì)象。把它看作是已經(jīng)實(shí)例化為單例的匿名內(nèi)部類。事實(shí)上,這個(gè)類并不是匿名的,它的名字是 Companion,但你可以省略這個(gè)名字。看到了嗎?這很簡(jiǎn)單。"
我很欣賞對(duì)象聲明的概念——單例很有用。但從語(yǔ)言中刪除靜態(tài)成員是不切實(shí)際的。在 Java 中我們使用靜態(tài) Logger 很經(jīng)典,它只是一個(gè) Logger,所以我們不關(guān)心面向?qū)ο蟮募兌?。它能夠工作,從?lái)沒(méi)有任何壞處。因?yàn)橛袝r(shí)候你必須使用靜態(tài)。舊版本 public static void main() 仍然是啟動(dòng) Java 應(yīng)用程序的唯一方式。
class AppRunner {
companion object {
@JvmStatic fun main(args: Array<String>) {
SpringApplication.run(AppRunner::class.java, *args)
}
}
}
import java.util.Arrays;
...
List<String> strings = Arrays.asList("Saab", "Volvo");
初始化地圖非常冗長(zhǎng),很多人使用 Guava:
import com.google.common.collect.ImmutableMap;
...
Map<String, String> string = ImmutableMap.of("firstName", "John", "lastName", "Doe");
在 Java 中,我們?nèi)匀辉诘却碌恼Z(yǔ)法來(lái)表達(dá)集合和映射。語(yǔ)法在許多語(yǔ)言中非常自然和方便。
const list = ['Saab', 'Volvo']
const map = {'firstName': 'John', 'lastName' : 'Doe'}
list = ['Saab', 'Volvo']
map = {'firstName': 'John', 'lastName': 'Doe'}
def list = ['Saab', 'Volvo']
def map = ['firstName': 'John', 'lastName': 'Doe']
簡(jiǎn)單來(lái)說(shuō),集合字面量的整齊語(yǔ)法就是你對(duì)現(xiàn)代編程語(yǔ)言的期望,特別是如果它是從頭開始創(chuàng)建的。Kotlin 提供了一系列內(nèi)置函數(shù),比如 listOf()、mutableListOf()、mapOf()、hashMapOf() 等等。
val list = listOf("Saab", "Volvo")
val map = mapOf("firstName" to "John", "lastName" to "Doe")
在地圖中,鍵和值與 to 運(yùn)算符配對(duì),這很好。但為什么一直沒(méi)有得到廣泛使用呢?令人失望。函數(shù)式語(yǔ)言(比如 Haskell)沒(méi)有空值。相反,他們提供 Maybe monad(如果你不熟悉monad,請(qǐng)閱讀 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 中沒(méi)有 Optional 的等價(jià)物,所以你大概應(yīng)該使用 Kotlin 的可空類型。讓我們來(lái)調(diào)查一下這個(gè)問(wèn)題。通常情況下,當(dāng)你有一個(gè) Optional 的時(shí)候,你想要應(yīng)用一系列無(wú)效的轉(zhuǎn)換。
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
}
上面的代碼是錯(cuò)誤的,parseInt() 會(huì)拋出 NPE 。map() 僅在有值時(shí)執(zhí)行。否則,Null 就會(huì)跳過(guò),這就是為什么 map() 如此方便。不幸的是,Kotlin 的 let 不會(huì)那樣工作。它從左側(cè)的所有內(nèi)容中調(diào)用,包括空值。為了保證這個(gè)代碼 Null 安全,你必須在每個(gè)代碼之前添加 let:
fun parseAndInc(number: String?): Int {
return number?.let { Integer.parseInt(it) }
?.let { it -> it + 1 } ?: 0
}
現(xiàn)在,比較 Java 和 Kotlin 版本的可讀性。你更傾向哪個(gè)?數(shù)據(jù)類是 Kotlin 在實(shí)現(xiàn) Value Objects 時(shí)使用的方法,以減少 Java 中不可避免的樣板問(wèn)題。例如,在 Kotlin 中,你只寫一個(gè) Value Object :
data class User(val name: String, val age: Int)
Kotlin 對(duì) equals()、hashCode()、toString() 以及 copy() 有很好的實(shí)現(xiàn)。在實(shí)現(xiàn)簡(jiǎn)單的DTO 時(shí)它非常有用。但請(qǐng)記住,數(shù)據(jù)類帶有嚴(yán)重的局限性。你無(wú)法擴(kuò)展數(shù)據(jù)類或者將其抽象化,所以你可能不會(huì)在核心模型中使用它們。這個(gè)限制不是 Kotlin 的錯(cuò)。在 equals() 沒(méi)有違反 Liskov 原則的情況下,沒(méi)有辦法產(chǎn)生正確的基于價(jià)值的數(shù)據(jù)。這也是為什么 Kotlin 不允許數(shù)據(jù)類繼承的原因。Kotlin 類默認(rèn)為 final。如果你想擴(kuò)展一個(gè)類,必須添加 open 修飾符。
open class Base
class Derived : Base()
Kotlin 將 extends 關(guān)鍵字更改為: 運(yùn)算符,該運(yùn)算符用于將變量名稱與其類型分開。那么再回到 C ++語(yǔ)法?對(duì)我來(lái)說(shuō)這很混亂。這里有爭(zhēng)議的是,默認(rèn)情況下類是 final。也許 Java 程序員過(guò)度使用繼承,也許應(yīng)該在考慮擴(kuò)展類之前考慮三次。但我們生活在框架世界,Spring 使用 cglib、jassist 庫(kù)為你的 bean 生成動(dòng)態(tài)代理。Hibernate 擴(kuò)展你的實(shí)體以啟用延遲加載。如果你使用 Spring,你有兩種選擇。你可以在所有 bean 類的前面添加 open,或者使用這個(gè)編譯器插件:
buildscript {
dependencies {
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-allopen', version: "$versions.kotlin"
}
}
如果你認(rèn)為自己有 Java 基礎(chǔ)就可以快速學(xué)習(xí) Kotlin,那你就錯(cuò)了。Kotlin 會(huì)讓你陷入深淵,事實(shí)上,Kotlin 的語(yǔ)法更接近 Scala。這是一項(xiàng)賭注,你將不得不忘記 Java 并切換到完全不同的語(yǔ)言。相反,學(xué)習(xí) Groovy 是一個(gè)愉快的過(guò)程。Java 代碼是正確的 Groovy 代碼,因此你可以通過(guò)將文件擴(kuò)展名從 .java 更改為 .groovy。學(xué)習(xí)新技術(shù)就像一項(xiàng)投資。我們投入時(shí)間,新技術(shù)讓我們得到回報(bào)。但我并不是說(shuō) Kotlin 是一種糟糕的語(yǔ)言,只是在我們的案例中,成本遠(yuǎn)超收益。以上內(nèi)容編譯自 From Java to Kotlin and Back Again,作者 Kotlin ketckup。他是一名具有15年以上專業(yè)經(jīng)驗(yàn)的軟件工程師,專注于JVM 。在 Allegro,他是一名開發(fā)團(tuán)隊(duì)負(fù)責(zé)人,JaVers 項(xiàng)目負(fù)責(zé)人,Spock 倡導(dǎo)者。此外,他還是 allegro.tech/blog 的主編。
本文一出就引發(fā)了業(yè)內(nèi)的廣泛爭(zhēng)議,Kotlin 語(yǔ)言擁護(hù)者 Márton Braun 就表示了強(qiáng)烈的反對(duì)。Márton Braun 十分喜歡 Kotlin 編程,目前他在 StackOverflow 上 Kotlin 標(biāo)簽的最高用戶列表中排名第三,并且是兩個(gè)開源 Kotlin 庫(kù)的創(chuàng)建者,最著名的是 MaterialDrawerKt。此外他還是 Autosoft 的 Android 開發(fā)人員,目前正在布達(dá)佩斯技術(shù)經(jīng)濟(jì)大學(xué)攻讀計(jì)算機(jī)工程碩士學(xué)位。當(dāng)我第一次看到這篇文章時(shí),我就想把它轉(zhuǎn)發(fā)出來(lái)看看大家會(huì)怎么想,我肯定它會(huì)是一個(gè)有爭(zhēng)議的話題。后來(lái)我讀了這篇文章,果然證明了它是一種主觀的、不真實(shí)的、甚至有些居高臨下的偏見。有些人已經(jīng)在原貼下進(jìn)行了合理的批評(píng),對(duì)此我也想表達(dá)一下自己的看法。“IDEA 團(tuán)隊(duì)”(或者 Kotlin 插件團(tuán)隊(duì))和“Kotlin 團(tuán)隊(duì)”肯定是同樣的人,我從不認(rèn)為內(nèi)部沖突會(huì)是個(gè)好事。語(yǔ)言提供這個(gè)功能給你,你需要的話就使用,如果討厭,調(diào)整檢查設(shè)置就是了。Kotlin 的類型推斷無(wú)處不在,作者說(shuō)的 Java 10 同樣可以簡(jiǎn)直是在開玩笑。Kotlin 的方式超越了推斷局部變量類型或返回表達(dá)式體的函數(shù)類型。這里介紹的這兩個(gè)例子是那些剛剛看過(guò)關(guān)于 Kotlin 的第一次介紹性講話的人會(huì)提到的,而不是那些花了半年學(xué)習(xí)該語(yǔ)言的人。例如,你怎么能不提 Kotlin 推斷泛型類型參數(shù)的方式?這不是 Kotlin 的一次性功能,它深深融入了整個(gè)語(yǔ)言。這個(gè)批評(píng)是對(duì)的,當(dāng)你與 Java 代碼進(jìn)行互操作時(shí),Null 安全性確實(shí)被破壞了。該語(yǔ)言背后的團(tuán)隊(duì)曾多次聲明,他們最初試圖使 Java 可為空的每種類型,但他們發(fā)現(xiàn)它實(shí)際上讓代碼變得更糟糕。Kotlin 不比 Java 更差,你只需要注意使用給定庫(kù)的方式,就像在 Java 中使用它一樣,因?yàn)樗](méi)有不去考慮 Null 安全。如果 Java 庫(kù)關(guān)心 Null 安全性,則它們會(huì)有許多支持注釋可供添加。也許可以添加一個(gè)編譯器標(biāo)志,使每種 Java 類型都可以為空,但這對(duì) Kotlin 團(tuán)隊(duì)來(lái)說(shuō)不得不花費(fèi)大量額外資源。:: class 為你提供了一個(gè) KClass 實(shí)例,以便與 Kotlin 自己的反射 API 一起使用,而:: class.java為你提供了用于 Java 反射的常規(guī) Java 類實(shí)例。為了清楚起見,顛倒的順序是存在的,這樣你就可以以合理的方式省略顯式類型。冒號(hào)只是語(yǔ)法,這在現(xiàn)代語(yǔ)言中是相當(dāng)普遍的一種,比如 Scala、Swift 等。我不知道作者在使用什么 IntelliJ,但我使用的變量名稱和類型都能夠自動(dòng)補(bǔ)全。對(duì)于參數(shù),IntelliJ 甚至?xí)o你提供相同類型的名稱和類型的建議,這實(shí)際上比 Java 更好。有時(shí)候你必須使用靜態(tài)。舊版本 public static void main() 仍然是啟動(dòng) Java 應(yīng)用程序的唯一方式。
class AppRunner {
companion object {
@JvmStatic fun main(args: Array<String>) {
SpringApplication.run(AppRunner::class.java, *args)
}
}
}
實(shí)際上,這不是啟動(dòng) Java 應(yīng)用程序的唯一方式。你可以這樣做:
fun main(args:Array <String>){ SpringApplication.run(AppRunner :: class.java,* args)}
fun main(args:Array <String>){ runApplication <AppRunner>(* args)}
你可以在注釋中使用數(shù)組文字。但是,除此之外,這些集合工廠的功能非常簡(jiǎn)潔,而且它們是另一種“內(nèi)置”到該語(yǔ)言的東西,而它們實(shí)際上只是庫(kù)函數(shù)。你只是抱怨使用:進(jìn)行類型聲明。而且,為了獲得它不必是單獨(dú)的語(yǔ)言結(jié)構(gòu)的好處,它只是一個(gè)任何人都可以實(shí)現(xiàn)的功能。如果你喜歡 Optional ,你可以使用它。Kotlin 在 JVM 上運(yùn)行。
對(duì)于代碼確實(shí)這有些難看。但是你不應(yīng)該在 Kotlin 代碼中使用 parseInt,而應(yīng)該這樣做(我不知道你使用該語(yǔ)言的 6 個(gè)月中為何錯(cuò)過(guò)這個(gè))。你為什么要明確地命名一個(gè) Lambda 參數(shù)呢?這個(gè)限制不是 Kotlin 的錯(cuò)。在 equals() 沒(méi)有違反 Liskov 原則的情況下,沒(méi)有辦法產(chǎn)生正確的基于價(jià)值的數(shù)據(jù)。這就是為什么 Kotlin 不允許數(shù)據(jù)類繼承的原因。
我不知道你為什么提出這個(gè)問(wèn)題。如果你需要更復(fù)雜的類,你仍然可以創(chuàng)建它們并手動(dòng)維護(hù)它們的 equals、hashCode 等方法。數(shù)據(jù)類僅僅是一個(gè)簡(jiǎn)單用例的便捷方式,對(duì)于很多人來(lái)說(shuō)這很常見。作者再次鄙視了,對(duì)此我實(shí)在無(wú)話可說(shuō)。作者認(rèn)為學(xué)習(xí) Kotlin 很難, 但是我個(gè)人并不這么認(rèn)為。從作者列舉的例子中,我感覺他只是了解語(yǔ)言的表面。很難想象他對(duì)此有投入很多時(shí)間。最近的語(yǔ)言排行,java依然排在第一,在語(yǔ)言排行榜上,Koltin已被甩到10名之外!關(guān)注公眾號(hào)【Java技術(shù)江湖】后回復(fù)“PDF”即可領(lǐng)取200+頁(yè)的《Java工程師面試指南》
強(qiáng)烈推薦,幾乎涵蓋所有Java工程師必知必會(huì)的知識(shí)點(diǎn),不管是復(fù)習(xí)還是面試,都很實(shí)用。
