前言

自从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 强制非空校验

// 所有变量默认不能为空
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("自定义异常")

可以看到,Kotlin通过强制非空校验机制,规避了Java最易犯的NPE问题,并且在不额外增加判空代码的情况下,很容易写出空安全的代码,当然也通过非空断言保留了抛出NPE的途径,具体请参考Kotlin官方文档-空安全

2.2 只读变量

// 通过val可以方便声明只读变量,防止变量的误修改,类似于java的final
val a = "Hello"
//...
a = "world" //编译错误
// var用于声明可读写变量
var b = 1 //如果下方没有代码对b进行修改,则编译告警

声明只读变量的语法更简洁,配合var未修改编译告警,强制开发人员明白无误地声明变量

3. 简洁高效,代码量可减少逾50%

3.1 默认参数与数据类

// 默认参数,大多数情况可以代替函数重载
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代码

默认参数、数据类、主构造函数的组合使用,让我们节省了90%以上的实体声明代码,且表达能力更清晰,可维护性更强

3.2 类型推断

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

在类型判断代码之后,变量会自动转换为需要的类型,省去变量声明和类型转换代码。

除此之外Kotlin拥有着丰富的集合操作,以及类扩展、更简洁的lambda、字符串模板、操作符重载、解构等等几乎包含了现在语言的所有优良特征,让业务代码节省50%以上,且表义能力更强。

4. Spring的加成

Spring-Boot已经将kotlin提升为仅次于Java的推荐语言,足以见得Spring对Kotlin重视

4.1 构造函数注入

Spring构造函数注入配置kotlin的主构造函数语法,Spring+Kotlin天生就很搭

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

4.2 针对Kotlin的扩展
Spring中还有部分专门针对Kotlin的扩展如: SpringApplicationExtension:

@SpringBootApplication
class SpringBootApplicationStarter

fun main(args: Array<String>) {
  //拉起Spring-Boot应用
  runApplication<SpringBootApplicationStarter>(*args)
}

4.3 针对Kotlin的示例代码

Spring文档专门为Kotlin编写了示例代码,这可是Spring支持的老牌语言Groovy多年都没享受到的待遇: 

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

@SpringBootApplication
open class SpringBootApplicationStarter

@Configuration
open class BeanConfig

@Service
open class TestService

@RestController
open class TestController

在IDE之外,Kotlin能完美地运行于Maven和Gradle之上,CI/CD工具Jenkins自然不在话下,JUnit、Swagger等Java原有工具链运行起来都没有差别,在字节码和jar包之外的各种工具能否运行,理论上并不取决于项目代码是Kotlin还是Java;可能最大的问题在于,原有的代码检查工具不能再继承使用。

2. 编码规范

Kotlin放开了很多限制如:

  • 可以声明全局变量和全局方法
  • 一个文件可以有多个类
  • 可以通过扩展来为已有类增加新方法

这带来了诸多便利,如同一领域的实体可以声明在一个文件中:

//WebResponses.kt
open class WebResponse(var errorCode: Int = 0, var errorMsg: String? =www.yasenyulee.cn null)

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

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

可以很方便地扩展已有类库

// StringExtensions.kt
val REGEX_Digits = Regex(www.shentuylgw.cn"\\\d+")
fun String.isDigits(): Boolean {
    return this.matches(REGEX_Digits)
}
// ACLInterceptor
val appId = request.getParameter("appId")
if (!appId.isNullOrBlank() && appId.isDigits()) {
//...
}

但是如果不加限制,很难保证开发人员不随心所欲。所以应该针对Kotlin项目制定一些编码规范(可以与Android项目共用),如:

  • 只有数据实体可以定义在同一个文件中
  • 类型扩展应以”Extensions“后缀结尾,如"StringExtensions"
  • 业务逻辑不应出现在全局方法中,应按照一类一文件的方式组织
  • 业务逻辑的扩展,不应使用Kotlin类型扩展机制,而应使用接口、抽象类、子类

3. 人员技能

对Java开发人员来说,学习Kotlin并不是什么困难的事情,以我个人的经验,直接以一个小项目开始的情况下,一周之内编码效率就会明显超过Java。

当然,语言工具不是项目成败的决定性因素,使用工具的人才是,不妨先让项目组中的核心成员去了解下Kotlin,再做决定。

快速上手建议从Koans开始,想要更深入地掌握,还是要阅读Kotlin官方文档

4. 依赖大小

在Spring项目中使用Kotlin会使依赖增加4.18MB左右 

这在Spring-Boot兴起之前,对我来说确实是个问题,6年前一个war包也就3~5MB大小,还要为Tomcat能部署多个服务而不会代码空间溢出,把war包进一步瘦身——把公共jar包都放入Tomcat/lib之下。

而现在Spring-Boot项目动则30MB起步的大小,让我已经不再考虑服务器资源的问题,而更享受各种组件和工具带来的开发效率提升。

当然这个问题,因项目实际情况而异,需要交给作为架构师的你自己去决策。

5. BenchMark

具体细节本不是此文的讨论目标,且对Kotlin做BenchMark性能测试不是那么困难,但是Kotlin的BenchMark资料也不多,这里我附送一个简明教程:

  • 步骤1:使用maven生成Kotlin benchmark工程
mvn archetype:generate www.wangffzc.cn \
          -DinteractiveMode=false \
          -DarchetypeGroupId=org.openjdk.jmh  www.yixingylzc.cn  \
          -DarchetypeArtifactId=jmh-kotlin-benchmark-archetype \
          -DgroupId=org.sample www.sangyulpt.com\
          -DartifactId=test \
          -Dversion=1.0
  • 步骤2:编写测试代码
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
open class MyBenchmark www.jinniuyulzc.cn{
	@Benchmark
	fun testMethod1(www.jiniuyLzc.cn ) {
		//...
	}

	@Benchmark
	fun testMethod2(www.baishenjzc.cn   ) {
		//...
	}
}
  • 步骤3:编译&运行
mvn clean package
java www.henxingyule.com -jar target/benchmarks.jar

更多性能测试详情,可参考

  • OpenJDK:jmh官网
  • kotlin-benchmarks项目

哪些项目类型适合使用Kotlin

  1. 现有Java项目,不推荐;
  2. 小型或验证型项目,强烈推荐;
  3. 微服务项目,推荐先局部试点,逐步完善编码规范和人员技能;
  4. 大型单体项目,有较强较稳定的核心团队时推荐,人员主靠招聘时不推荐;
  5. 公共组件,不推荐,除专门用于Kotlin的组件外。

结语

不管怎么说,语言工具不是项目成败的决定性因素,使用工具的人才是,不妨先让项目组中的核心成员去了解下Kotlin,再做决定。

从架构师视角看是否该用Kotlin做服务端开发?的更多相关文章

  1. 成为JAVA架构师必看书籍推荐

    原创文章 “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书 ...

  2. Java架构师必看,超详细的架构师知识点分享!

    在Java程序员行业中,有不少Java开发人员的理想是成为一名优秀的Java架构师,Java架构师的主要任务不是从事具体的软件程序的编写,而是从事更高层次的开发构架工作.他必须对开发技术非常了解,并且 ...

  3. 【架构师之路】APP架构师必看:面对爆发流量如何进行架构调整

    一.APP架构与WEB架构的最大不同 移动APP的架构和传统PC的WEB架构有三点不同: 1.连接的稳定性.在传统的web端连接成功后就可以认为它是稳定的,但在移动端.无线端,APP连接非常敏感,可能 ...

  4. Java架构师必看的10本书

    1.大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍. 从分布式系统的知识切入,让读者对分布式系统有基本的了解:然后介绍大型网站随着数据量.访问量增长 ...

  5. 你真的了解微服务架构吗?听听八年阿里架构师怎样讲述Dubbo和Spring Cloud微服务架构

    微服务架构是互联网很热门的话题,是互联网技术发展的必然结果.它提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.虽然微服务架构没有公认的技术标准和规范或者草案,但业 ...

  6. 听听八年阿里架构师怎样讲述Dubbo和Spring Cloud微服务架构

    转自:https://baijiahao.baidu.com/s?id=1600174787011483381&wfr=spider&for=pc 微服务架构是互联网很热门的话题,是互 ...

  7. go语言游戏服务端开发(一)——架构

    五邑隐侠,本名关健昌,12年游戏生涯. 本教程以Go语言为例.   网络游戏程序分为客户端和服务端.客户端负责图形渲染.交互和一些简单校验处理,服务端负责业务逻辑处理.数据存储. 我们开发一个游戏de ...

  8. .NET应用架构设计—服务端开发多线程使用小结(多线程使用常识)

    有一段时间没有更新博客了,最近半年都在着写书<.NET框架设计—大型企业级框架设计艺术>,很高兴这本书将于今年的10月份由图灵出版社出版,有关本书的具体介绍等书要出版的时候我在另写一篇文行 ...

  9. Java 架构师之路(2)

    一.技术 J2EE技术是架构师的基础.1.<Java编程思想> 初学Java时阅读这本书觉得好难,阅读第二遍时才觉得讲的很细致.这是一本不怕多读的好书. 2.<J2EE应用与BEA ...

随机推荐

  1. java环境变量修改后不生效

    修改java环境变量后,cmd查看java版本,还是之前的版本,需要做以下处理: 1. 删除C:\Windows\System32目录下的相关的java.exe.javaw.exe.javaws.ex ...

  2. Ternsorflow 学习:006-MNIST进阶 深入MNIST

    前言 这篇文章适合实践过MNIST入门的人学习观看.没有看过MNIST基础的人请移步这里 深入MNIST TensorFlow是一个非常强大的用来做大规模数值计算的库.其所擅长的任务之一就是实现以及训 ...

  3. Java入门基础(类的方法)

    方法 1.添加方法 方法表示一个类能做什么.在Java里,方法和属性属于对等的术语,在一个类中,不仅可以添加属性,还可以添加方法. 类 { 属性:描述“我有什么” 方法:描述“我能做什么” } 例子: ...

  4. day07-Python运维开发基础(深/浅拷贝、字典/集合/相关操作)

    1. 深拷贝与浅拷贝 # ### 深拷贝 和 浅拷贝 """ a = 7 b = a a = 8 print(b) lst1 = [1,2,3] lst2 = lst1 ...

  5. CentOS 7安装/卸载Redis,配置service服务管理

    Redis简介 Redis功能简介 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 相比于传统的关系型数据库,Redis的存储方式是key-va ...

  6. Express 应用程序生成器

    通过应用生成器工具 express-generator 可以快速创建一个应用的骨架. express-generator 包含了 express 命令行工具.通过如下命令即可安装: $ npm ins ...

  7. java核心-多线程(8)- 并发原子类

        使用锁能解决并发时线程安全性,但锁的代价比较大,而且降低性能.有些时候可以使用原子类(juc-atomic包中的原子类).还有一些其他的非加锁式并发处理方式,我写这篇文章来源于Java中有哪些 ...

  8. numpy.linspace使用详解

    numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None) 在指定的间隔内返回均匀间隔的数字. 返回nu ...

  9. android studio 入门坑

    安装 android studio,碰到下面这个图片,直接跳过. 安装时候,选择自定义设置,里面可以配置 sdk 的存放位置. 新建工程后,gradle sync 比较慢,可以 修改工程中的 buil ...

  10. redis.conf文件配置

    最重要三个配置 1. bind 127.0.0.1 需要注释掉这一行,使别的主机可以访问 2. daemonize no 需要改为yes,使其后台运行 3. requirepass foobared ...