從架構師視角看是否該用Kotlin做服務端開發?

前言

自從Oracle收購Sun之後,對Java收費或加強控制的嘗試從未間斷,谷歌與Oracle圍繞Java API的官司也跌宕起伏。雖然Oracle只是針對Oracle JDK8的升級收費,並釋放了OpenJDK一直開源這份善意,但是如果沒有各個大非Oracle的JVM、JDK和眾多其它基於JVM的語言,Oracle這份善意能維持到什麼時候可不好說。

大廠要從JVM和JDK的層面早做打算,而廣大中小企業,就只能先從Java語言的層面,先找到Oracle以外的備胎。自從被谷歌欽定為Android開發首選語言之後,採用Apache2.0 License的Kotlin逐漸進入大眾的視野。從領域語言到通用語言,基於JVM的語言選擇眾多,Kotlin能脫穎而出被谷歌相中,除了License的友好外,自然有其獨到之處(個人覺得基於Python語法的Jython在當時也算是一個強強聯合的選擇,當然現在來看Kotlin的優勢明顯)。我們先從摘自網上的一個段子來感受下Kotlin的特點:

  • Scala:想解決Java表達能力不足的問題
  • Groovy:想解決Java語法過於冗長的問題
  • Clojure:想解決Java沒有函數式編程的問題
  • Kotlin:想解決Java

當然,團隊開發比個人開發要考慮的問題更多,本文從研發團隊的視角,來審視在服務端開發項目中是否應該使用Kotlin。而關於Kotlin語言細節,詳情請大家參移步Kotlin官方文檔或搜索引擎去了解更多,本文就不詳細展開。

選擇Kotlin的理由

1. 與Java近乎完美的兼容

作為團隊技術預研,我已小試牛刀使用Kotlin做了一小一中真實上線項目,包括CI\\CD、Spring全家桶、MyBatis、RDMS、NoSQL、消息隊列、微服務、RESTful接口、鑑權、計費等全面實踐,除項目代碼之外的也用Kotlin實現了爬蟲、運維、測試工具等。這其中除了BenchMark性能測試因OpenJDK:jmh直接操作字節碼而需要編譯比java略複雜外,所有事件都能做到人(Java)無我(Kotlin)有,人(Java)有我(Kotlin)強,只要十天左右就能高質量從零完成一套AI SaaS服務的開發、測試。

我自身的實踐可以說明,Kotlin使用Java的開源組件、類庫、工具棧上與Java代碼本身幾乎(僅發現jmh有些許差別外)沒有差別,這是其它基於JVM的語言無法比擬有優勢。

2. 更易寫出可靠的代碼

2.1 強制非空校驗

<code>// 所有變量默認不能為空
var a: String = "abc"
a = null //編譯錯誤
// 如果想要允許變量為空,變量聲明中的類型以"?"結尾
var b: String? = null
// 非空類型的變量,可以直接調用對象方法
println(a.length)
// 可空類型的變量,可以用"?."替換"."來進行完全調用
println(b?.length) //如果b為null,則b?.length返回null,而不會拋出NPE

// "?."鏈式調用更顯安全調用的威力
bob?.department?.head?.name //只要有一環為null,則整個表達式返回null
// 空類型不能直接賦值給非空類型
a = b //編譯錯誤
// 可以使用非空斷言"!!"來強制將b轉換為其對應的非空類型
a = b!! //如果b為null,則拋出NPE,否則賦值成功
// 更安全的做法是使用"?:"賦值
a = b?:"Default"
// "?:"還可以接”throw“、"return"、"break"語句來提前結束函數或語句塊
a = b?: throw MyException("自定義異常")
/<code>

可以看到,Kotlin通過強制非空校驗機制,規避了Java最易犯的NPE問題,並且在不額外增加判空代碼的情況下,很容易寫出空安全的代碼,當然也通過非空斷言保留了拋出NPE的途徑,具體請參考Kotlin官方文檔-空安全

2.2 只讀變量

<code>// 通過val可以方便聲明只讀變量,防止變量的誤修改,類似於java的final
val a = "Hello"
//...
a = "world" //編譯錯誤
// var用於聲明可讀寫變量
var b = 1 //如果下方沒有代碼對b進行修改,則編譯告警
/<code>

聲明只讀變量的語法更簡潔,配合var未修改編譯告警,強制開發人員明白無誤地聲明變量

3. 簡潔高效,代碼量可減少逾50%

3.1 默認參數與數據類

<code>// 默認參數,大多數情況可以代替函數重載
fun a(name: String, age: Int = 0) {
//...
}

// data關鍵字會自動為類型增加hashCode(),equals(),copy(),toString()方法
// val關鍵字會自動生成只讀成員變量及get方法
// var關鍵字會自動生成成員變量及get和set方法
data class Person(val id: Int, var name: String, var age: Int = 0)
//以上就是Person實體的完整聲明,它等價於十行以上的java代碼
/<code>

默認參數、數據類、主構造函數的組合使用,讓我們節省了90%以上的實體聲明代碼,且表達能力更清晰,可維護性更強

3.2 類型推斷

<code>when(val u = request.session.getAttribute("user")){
is String -> println(u.toUpperCase())
is Map -> println(u["name"])
!is List -> println(u)
}
/<code>

在類型判斷代碼之後,變量會自動轉換為需要的類型,省去變量聲明和類型轉換代碼。

除此之外Kotlin擁有著豐富的集合操作,以及類擴展、更簡潔的lambda、字符串模板、操作符重載、解構等等幾乎包含了現在語言的所有優良特徵,讓業務代碼節省50%以上,且表義能力更強。

4. Spring的加成

Spring-Boot已經將kotlin提升為僅次於Java的推薦語言,足以見得Spring對Kotlin重視

4.1 構造函數注入

Spring構造函數注入配置kotlin的主構造函數語法,Spring+Kotlin天生就很搭

<code>@Service
class SMSService(
@Value("\\${sms.appId}") private val appId: String,
@Value("\\${sms.appSecret}") private val appSecurity: String,
private val webClient: WebClient
) {
//...
}
/<code>

4.2 針對Kotlin的擴展Spring中還有部分專門針對Kotlin的擴展如: SpringApplicationExtension:

<code>@SpringBootApplication
class SpringBootApplicationStarter

fun main(args: Array<string>) {
//拉起Spring-Boot應用
runApplication<springbootapplicationstarter>(*args)
}
/<springbootapplicationstarter>/<string>/<code>

4.3 針對Kotlin的示例代碼

Spring文檔專門為Kotlin編寫了示例代碼,這可是Spring支持的老牌語言Groovy多年都沒享受到的待遇:

從架構師視角看是否該用Kotlin做服務端開發?

Kotlin天生對Spring的友好,以及Spring對Kotlin的重視和加成,可以預見Kotlin未來在服務器開發領域的地位會越來越高。

選擇Kotlin需要考慮的問題

1. IDE&工具鏈

Kotlin首選開發工具非同屬jetbrain公司的IDEA莫屬Eclipse+Kotlin Plugin也是不錯的選擇二者Kotlin的開發體驗都不輸於Java的開發體驗不過Eclipse的Kotlin插件存在兩個問題:1) Debug不能自動識別Kotlin的main方法,需要手工填入Class名;2) Debug不支持Kotlin編譯的allopen選項,所有Bean和Configuration類型須顯示聲明為open

<code>@SpringBootApplication
open class SpringBootApplicationStarter

@Configuration
open class BeanConfig

@Service
open class TestService

@RestController
open class TestController
/<code>

在IDE之外,Kotlin能完美地運行於Maven和Gradle之上,CI/CD工具Jenkins自然不在話下,JUnit、Swagger等Java原有工具鏈運行起來都沒有差別,在字節碼和jar包之外的各種工具能否運行,理論上並不取決於項目代碼是Kotlin還是Java;可能最大的問題在於,原有的代碼檢查工具不能再繼承使用。

2. 編碼規範

Kotlin放開了很多限制如:

  • 可以聲明全局變量和全局方法
  • 一個文件可以有多個類
  • 可以通過擴展來為已有類增加新方法

這帶來了諸多便利,如同一領域的實體可以聲明在一個文件中:

<code>//WebResponses.kt
open class WebResponse(var errorCode: Int = 0, var errorMsg: String? = null)

open class DataResponse(var data: T?) : WebResponse()

open class MapResponse(map: Map) : DataResponse>(map) {
constructor(vararg pairs: Pair) : this(mutableMapOf(*pairs))
}
/<code>

可以很方便地擴展已有類庫

<code>// StringExtensions.kt
val REGEX_Digits = Regex("\\\\\\d+")
fun String.isDigits(): Boolean {
return this.matches(REGEX_Digits)
}
// ACLInterceptor
val appId = request.getParameter("appId")

if (!appId.isNullOrBlank() && appId.isDigits()) {
//...
}
/<code>

但是如果不加限制,很難保證開發人員不隨心所欲。所以應該針對Kotlin項目制定一些編碼規範(可以與Android項目共用),如:

  • 只有數據實體可以定義在同一個文件中
  • 類型擴展應以”Extensions“後綴結尾,如"StringExtensions"
  • 業務邏輯不應出現在全局方法中,應按照一類一文件的方式組織
  • 業務邏輯的擴展,不應使用Kotlin類型擴展機制,而應使用接口、抽象類、子類

3. 人員技能

對Java開發人員來說,學習Kotlin並不是什麼困難的事情,以我個人的經驗,直接以一個小項目開始的情況下,一週之內編碼效率就會明顯超過Java。

當然,語言工具不是項目成敗的決定性因素,使用工具的人才是,不妨先讓項目組中的核心成員去了解下Kotlin,再做決定。

快速上手建議從Koans開始,想要更深入地掌握,還是要閱讀Kotlin官方文檔

4. 依賴大小

在Spring項目中使用Kotlin會使依賴增加4.18MB左右

從架構師視角看是否該用Kotlin做服務端開發?

這在Spring-Boot興起之前,對我來說確實是個問題,6年前一個war包也就3~5MB大小,還要為Tomcat能部署多個服務而不會代碼空間溢出,把war包進一步瘦身——把公共jar包都放入Tomcat/lib之下。

而現在Spring-Boot項目動則30MB起步的大小,讓我已經不再考慮服務器資源的問題,而更享受各種組件和工具帶來的開發效率提升。

當然這個問題,因項目實際情況而異,需要交給作為架構師的你自己去決策。

5. BenchMark

具體細節本不是此文的討論目標,且對Kotlin做BenchMark性能測試不是那麼困難,但是Kotlin的BenchMark資料也不多,這裡我附送一個簡明教程:

  • 步驟1:使用maven生成Kotlin benchmark工程
<code>mvn archetype:generate \\
-DinteractiveMode=false \\
-DarchetypeGroupId=org.openjdk.jmh \\
-DarchetypeArtifactId=jmh-kotlin-benchmark-archetype \\
-DgroupId=org.sample \\
-DartifactId=test \\
-Dversion=1.0
/<code>
  • 步驟2:編寫測試代碼
<code>@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
open class MyBenchmark {
\t@Benchmark
\tfun testMethod1() {
\t\t//...
\t}

\t@Benchmark
\tfun testMethod2() {
\t\t//...
\t}
}
/<code>
  • 步驟3:編譯&運行
<code>mvn clean package
java -jar target/benchmarks.jar
/<code>

更多性能測試詳情,可參考

  • OpenJDK:jmh官網
  • kotlin-benchmarks項目

哪些項目類型適合使用Kotlin

  1. 現有Java項目,不推薦;
  2. 小型或驗證型項目,強烈推薦;
  3. 微服務項目,推薦先局部試點,逐步完善編碼規範和人員技能;
  4. 大型單體項目,有較強較穩定的核心團隊時推薦,人員主靠招聘時不推薦;
  5. 公共組件,不推薦,除專門用於Kotlin的組件外。

結語

不管怎麼說,語言工具不是項目成敗的決定性因素,使用工具的人才是,不妨先讓項目組中的核心成員去了解下Kotlin,再做決定。

文章來源:https://my.oschina.net/u/4254626/blog/3165933

關注我瞭解更多程序員資訊技術,想免費領取豐富架構資料,可以私信我【Java】


分享到:


相關文章: