为何应将微服务从Java迁移到Kotlin:经验与见解【来自DZone】

为何应将微服务从Java迁移到Kotlin:经验与见解

了解为何向Kotlin迁移如此成功,以及为何开发者们即便此前仅接触过其他JVM语言,仍热衷于转向这门语言。

我就职于东欧最大的一家私人银行,负责开发一款移动应用的后端。我们的集群由400多个微服务组成,个别服务的峰值负载能达到五位数。最初向微服务架构转型时,我们所有的代码都是用Java编写的。但随着时间推移,我们开始积极将微服务迁移到Kotlin。如今,所有新的微服务都只用Kotlin创建,Java代码的占比已降至20%以下。

在本文中,我将解释为何向Kotlin的迁移如此成功,以及为何开发者们即便此前仅接触过其他JVM语言,仍热衷于转向这门语言。

Kotlin与Java的对比

Kotlin的主要优势之一是与Java代码完全兼容。Kotlin能与Java类和方法无缝交互,反之亦然,这使得向新语言的过渡十分顺畅,无需重写现有代码。Kotlin使用JVM字节码,确保了与我们项目中大量使用的所有Java库和框架的兼容性。

微服务的模块化结构非常适合逐步引入Kotlin。我们先使用Kotlin开发新组件,然后慢慢迁移旧组件。重要的是,我们可以继续使用Java代码,而无需担心冲突或故障。这种方法让我们规避了重大风险,确保了整个集群的稳定运行。

Kotlin提供了一系列现代化特性,简化了开发流程并提高了开发效率。以下是几个例子:

  • 协程:便于高效编写异步代码,避免了Java多线程的复杂性。
  • 扩展:允许在不修改现有类的情况下为其添加新函数。
  • 可空类型 :解决了NullPointerException问题,这是Java开发者常遇到的难题 。

转向Kotlin既流畅又自然,因为它的语法与Java极为相似。然而,与Java不同的是,Kotlin省去了大量样板代码,使代码更加简洁易读。例如,在Kotlin中使用data class,只需一行代码就能创建POJO类。在语言的其他方面,如集合操作、异步编程和可空类型处理,也有类似的改进。

代码示例:Java与Kotlin

为了说明这一点,以下是一些对比:

POJO类与数据类

Java
java 复制代码
public class Person {
    private String name;
    private int age;
  
  /*
  此外,你还需要为每个字段添加构造函数、
  获取器和设置器,
  重写toString,
  equals,
  hashCode
  */
}

在Java中,创建一个简单的POJO(普通Java对象)需要显式定义字段、构造函数、获取器、设置器,并且通常要重写toString()equals()hashCode()方法。这导致产生大量样板代码。

Kotlin
kotlin 复制代码
data class Person(val name: String, val age: Int)

Kotlin的data class会自动生成构造函数、获取器(对于var属性还有设置器)、toString()equals()hashCode()copy()方法。这一行代码相当于Java中的几十行代码。

可空类型

Java
java 复制代码
String name = null;
if (name != null) {
    System.out.println(name.length());
} else {
    System.out.println("Name is null");
}

在Java中,访问对象前需要显式检查是否为null,以避免NullPointerException。这往往会导致冗长的空值检查代码。

Kotlin
kotlin 复制代码
val name: String? = null
println(name?.length ?: "Name is null")

Kotlin使用安全调用操作符?.和Elvis操作符?:简洁地处理可空类型。这行代码在name不为null时安全地访问其length属性,否则打印"Name is null"。在Kotlin中,安全调用操作符?.和用于默认值的?:操作符简化了空值处理。

嵌套对象检查

Java
java 复制代码
if (person != null && person.getAddress() != null && person.getAddress().getCity() != null) {
    System.out.println(person.getAddress().getCity());
}

在Java中,检查嵌套对象是否为null需要进行多次检查,这会导致嵌套很深的if语句或冗长的布尔表达式。

Kotlin
kotlin 复制代码
person?.address?.city?.let {
    println(it)
}

Kotlin的安全调用操作符?.允许链式调用可空对象。let函数仅在链式调用中所有前置调用都不为null时才执行代码块。

异步调用

Java
java 复制代码
public Mono<String> fetchData() {
    Mono<String> first = fetchFirst();
    Mono<String> second = fetchSecond();
    
    return Mono.zip(first, second)
            .map(tuple -> tuple.getT1() + " " + tuple.getT2());
}

这段Java代码使用Project Reactor的Mono进行异步编程。它异步获取两段数据并将其组合。

Kotlin
kotlin 复制代码
suspend fun fetchData() {
    val first = async { fetchFirst() }
    val second = async { fetchSecond() }

    println("${first.await()} ${second.await()}")
}

Kotlin的协程使异步代码在外观和行为上都类似同步代码。async函数启动协程来获取数据,await()则暂停执行,直到结果可用。

集合处理

Java
java 复制代码
List<String> names = Arrays.asList("John", "Jane", "Doe");
List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("J"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

Java使用流和Lambda表达式处理集合。这段代码过滤出以"J"开头的名字,并将其转换为大写。

Kotlin
kotlin 复制代码
val names = listOf("John", "Jane", "Doe")
val filteredNames = names.filter { it.startsWith("J") }
                          .map { it.uppercase() }

Kotlin为集合处理提供了简洁的函数。这段代码与Java版本功能相同,但语法更易读。Kotlin在处理集合时提供了更简洁、更具表现力的语法。

Java中的if-else与Kotlin中的when

Java
java 复制代码
int number = 3;
String result;

if (number == 1) {
    result = "One";
} else if (number == 2) {
    result = "Two";
} else {
    result = "Other";
}

Java使用传统的if-else语句进行条件逻辑判断。当条件较多时,代码会变得冗长。

Kotlin
kotlin 复制代码
val number = 3
val result = when (number) {
    1 -> "One"
    2 -> "Two"
    else -> "Other"
}

Kotlin的when表达式为switch语句和复杂的if-else链提供了更简洁、更强大的替代方案。

类扩展

Java
java 复制代码
public class StringUtils {
    public static String reverse(String s) {
        return new StringBuilder(s).reverse().toString();
    }
}

在Java中,为现有类添加功能通常需要创建包含静态方法的工具类。

Kotlin
kotlin 复制代码
fun String.reverse(): String = this.reversed()

Kotlin的扩展函数允许在不修改现有类源代码的情况下为其添加新方法。这个函数用于反转字符串,并且可以直接在字符串对象上调用。

kotlin 复制代码
val reversed = "Hello".reverse()  // "olleH"

扩展函数可以像字符串类的方法一样被调用,使代码更直观、更具面向对象特性。

这些示例清晰地展示了,与Java相比,Kotlin如何通过简洁的语法、内置特性以及强大的异步编程和集合处理工具,简化并增强开发过程。

结论

说到在同一项目中Java和Kotlin的兼容性,我想强调的是,在项目中同时使用这两种语言编写微服务,无需创建单独的库和启动器。唯一的限制是Java版本,其不能高于服务中使用的版本。Spring也完全支持这两种语言,使用上的差异仅在于需要通过Maven或Gradle构建系统引入的部分依赖项。

在我们的项目中同时使用Java和Kotlin数年后,我可以自信地说,开发者们不想再回到只用Java的时代。他们更喜欢Kotlin,因为它更简洁、更具表现力,为编写高效代码提供了更多可能。Kotlin代码更易于阅读和理解,尤其是在定期审查同事的拉取请求时。对于有其他JVM语言经验的程序员来说,转向Kotlin的速度非常快。

当然,Java也在不断发展。在新版本中,出现了像记录(records)这样的特性,它类似于Kotlin中的数据类。虚拟线程(Virtual threads)也在开发中,其可能会取代协程。此外,对可空对象的支持也在不断改进。

尽管如此,我们的大多数开发者还是更喜欢使用Kotlin。他们欣赏Kotlin的简洁性、表现力以及能显著提高生产力和代码质量的现代化特性。对我们来说,从Java过渡到Kotlin既简单又自然,这使我们能够维护现有系统,同时逐步引入新语言。

相关推荐
MacroZheng7 分钟前
完美替代 Navicat!这款内置 AI 的数据库工具,太香了!
java·后端·mysql
SamDeepThinking33 分钟前
从REST到gRPC,一个API选型的思考框架
java·后端·程序员
Zane199436 分钟前
接口都能写默认实现了,为什么还需要抽象类
java·后端
未秃头的程序猿39 分钟前
读Spring AI源码的方法——不是硬啃,是带着问题去翻
java·后端·spring
用户3126874877201 小时前
别再 Thread.sleep 硬等了!并发工具类到底怎么选?
java
Java内核笔记1 小时前
万字长文剖析 Spring Boot 4 自动配置机制源码:从 @EnableAutoConfiguration 到条件装配
java·后端
evans在进步1 小时前
Spring MVC 核心机制详解:全局异常处理、请求跳转与数据绑定
java·spring·mvc
weixin_BYSJ19871 小时前
springboot小区物业管理小程序---附源码45555
java·javascript·spring boot·python·django·flask·php
coder_lorraine1 小时前
Halo 2.x 回收站文章无法删除?一篇文章教你彻底解决
java·运维