从 Java 代码构造 Kotlin class 实例

Constructing instance of Kotlin class from Java code

我有一个包含 15 个字段的 Kotlin 数据 class。当我使用主构造函数在我的 Kotlin 代码中构造此 class 的实例时,我可以利用有用的功能,例如:

但是,当从 Java 代码构建此 Kotlin class 的实例时,我面临着这样一个事实,即我需要在构造函数中以正确的顺序提供所有 15 个参数,而无需命名它们的能力。当这个 Java 代码是一个单元测试时,这尤其不方便,我对填写所有这些字段并不感兴趣,但只有一两个对测试有用。

当纯粹使用 Java 时,我不会遇到这个问题,因为我会使用 (Lombok) 构建器来构造对象的实例,可以灵活地只提供我感兴趣的字段英寸

有没有办法缓解这个问题,或者这是我混合使用 Java 和 Kotlin 必须付出的代价?

Is there a way to mitigate this problem...

有点,但它不是 out-of-the-box 解决方案。下面详细介绍它。

... is this the price I have to pay for mixing Java and Kotlin?

不,不是。如果我们解决这个问题,您会注意到 Java 无法提供默认值,因此在与 Kotlin 一起使用时不方便。构造复杂对象是Builder Pattern存在的主要原因。但是 Kotlin 为我们提供了一个解决方案,可以避免大多数与构造复杂对象相关的问题,并使构建器模式过时。

如何处理?

没有一种解决方案,而是多种解决方案。我会列出至少两个我马上想到的:

  1. 一级+二级构造;
  2. 工厂。

一级+二级构造

使用这个选项,你可以创建一个包含整个列表的主构造函数 您的 class 应该持有的参数并引入一个辅助构造函数,该构造函数 只接受必需的值或默认情况下无法设置的值 :

class Example(val param1: Any, val param2: Any? = null, val param3: Any? = null) {
    // In this situation we have to use different argument names
    // to make explicit use of primary constructor.
    // e.g. if we remove `param1 = ` or rename `requiredParam` to `param1`
    // we will get an error saying: "There's a cycle in the delegation calls chain" 
    constructor(requiredParam: Any) : this(param1 = requiredParam)
}

工厂

对于工厂来说,一切看起来都差不多。虽然,此解决方案 在 Java 中会更加冗长,但它 消除了使用命名参数 的需要,并为我们提供了 在对象初始化之前可以自由地做更多的准备工作我们甚至可以让它异步(就像是网络调用一样)!

class Example(val param1: Any, val param2: Any? = null, val param3: Any? = null) {
    object Factory {
        fun from(param1: Any): Example {
            return Example(param1)
        }
    }
}

结论

没有“正确”的解决方案。我们可以选一个我们喜欢的,也可以想出一个新的。

  1. 一级+二级构造函数:
    • (+) 从 Java;
    • 使用时更简洁
    • (-) 如果辅助构造函数发生变化,您将必须更新 Java 代码;
    • (-) 构造函数参数需要不同的名称。
  2. 工厂:
    • (+) 提供更多自由:例如你可以在 from 函数中计算一些东西,与构造函数相比它会更具可读性;
    • (+) 隐藏构造函数实现。您以后可以更改构造函数而无需修改任何 Java 代码或工厂函数;
    • (-) 在 Java.
    • 中使用时会更加冗长