是否可以使用 Groovy 中的命名参数(包括“copy”)调用 Kotlin 函数?

Would it be possible to call a Kotlin function with named parameters from Groovy (including `copy`)?

提问人:Robert Elliot 提问时间:10/17/2022 最后编辑:Robert Elliot 更新时间:10/19/2022 访问量:410

问:

Context - 带有 Spock/Groovy 测试的 Kotlin 代码库。

在我的 Groovy 测试中,使用名称调用带有命名参数的 Kotlin 函数会很方便。我特别将其用于实体的测试构建器。

我希望能够使用 Groovy 中的参数名称调用它们。

因此,鉴于这个 Kotlin:

data class User(
  val id: Id,
  val name: String,
  val emailAddress: String,
)

fun buildUser(
  id: Id = randomId(),
  name: String = randomName(),
  emailAddress: String = randomEmailAddress(),
): User

我希望能够写出这个时髦的东西:

User user = buildUser(
  name: 'My name'
)

感觉这应该是可能的;要么在 Kotlin 端使用编译器插件,使用采用 的版本重载方法,要么通过 Groovy 端的 AST 转换。有人这样做过吗?buildUserMap

这也可能允许以相同的方式在 上调用该方法:copydata class

User user = buildUser()
User userWithDifferentEmailAddress = user.copy(emailAddress: '[email protected]')

理想情况下,IntelliJ IDEA 会充分了解它,以便能够尊重重命名参数并从调用站点参数名称导航到接收站点参数,但这可能要求太多......

编辑 - 查看反编译的 Kotlin,它会创建以下方法:


public static User buildUser$default(Id var0, String var1, String var2, int var3, Object var4) {
  // uses var3 as a flag to indicate which fields were provided by the caller
}

public static User copy$default(User var0, Id var1, String var2, String var3, int var4, Object var5) {
  // uses var4 as a flag to indicate which fields were provided by the caller
}

所以应该可以争吵 Groovy 来称呼他们,我认为......

Kotlin Groovy Spock 命名参数

评论

0赞 daggett 10/17/2022
buildUser(name: 'My name')- 你有没有尝试过Groovy的相同语法?有什么错误吗?
1赞 Robert Elliot 10/17/2022
是的,当然:groovy.lang.MissingMethodException: No signature of method: static foo.ExperimentKt.buildUser() is applicable for argument types: (LinkedHashMap) values: [[name:My name]] Possible solutions: buildUser(foo.Id, java.lang.String, java.lang.String)
0赞 daggett 10/17/2022
我看不到使用 Groovy 的 Kotlin 命名参数的方法,除非您提供接受映射的方法
0赞 Robert Elliot 10/17/2022
您对 Groovy AST 转换有很多经验吗?我本来以为他们会允许这样做。如果没有,Kotlin 编译器插件肯定可以生成采用 Map 的方法的重载版本。
0赞 Leonard Brünings 10/18/2022
为什么不使用 Groovy 的支持?@NamedVariant@NamedVariant User buildUser(Id id = randomId(), String name = randomName(), String emailAddress = randomEmailAddress()) { new User (id, name, emailAddress )}

答:

0赞 Leonard Brünings 10/19/2022 #1

我只能提供部分解决方案。通过 Groovy 的变换,您可以在 Groovy 中像这样声明您的工厂函数。@NamedVariant

@NamedVariant
User buildUser(
  Id id = randomId(),
  String name = randomName(),
  String emailAddress = randomEmailAddress()) {
    new User (id, name, emailAddress )
}

它将生成一个重载,该重载采用映射或参数,因此您可以按照描述的方式使用它们。

至于在普通的时髦中无事可做。copy

您可以尝试编写 AST 转换,但您将依赖于 kotlin 编译器实现来了解如何生成方法以及如何设置标志。我认为尝试生成复制行为的自定义 groovy 扩展函数会更容易。查看扩展模块