为何应将微服务从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既简单又自然,这使我们能够维护现有系统,同时逐步引入新语言。

相关推荐
matlabgoodboy2 分钟前
代码编写java代做matlab程序代编Python接单c++代写web系统设计
java·python·matlab
moton201713 分钟前
云原生:构建现代化应用的基石
后端·docker·微服务·云原生·容器·架构·kubernetes
liuyunshengsir29 分钟前
Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能
java·spring boot·prometheus
路上阡陌38 分钟前
Java学习笔记(二十四)
java·笔记·学习
何中应1 小时前
Spring Boot中选择性加载Bean的几种方式
java·spring boot·后端
苏苏大大1 小时前
zookeeper
java·分布式·zookeeper·云原生
wclass-zhengge1 小时前
03垃圾回收篇(D3_垃圾收集器的选择及相关参数)
java·jvm
涛ing1 小时前
23. C语言 文件操作详解
java·linux·c语言·开发语言·c++·vscode·vim
5xidixi2 小时前
Java TCP协议(2)
java·tcp/ip
2013crazy2 小时前
Java 基于 SpringBoot+Vue 的校园兼职平台(附源码、部署、文档)
java·vue.js·spring boot·兼职平台·校园兼职·兼职发布平台