Kotlin 反射:当通过泛型知道类型时,是否可以使用 KProperty1 并省略该类

发布于 2025-01-14 08:12:52 字数 1374 浏览 0 评论 0原文

我正在从事大规模数据库转换。有很多数据类,其中一些数据类之间的关系类似于 SQL 数据库中的关系。这些关系是通过 UUID 完成的,而不是通过嵌套数据类完成的。

为了在列表中通过另一个属性中的某个值查找此类 ID,我使用了一个名为“lookup”的函数,该函数采用我具有值的字段的 KProperty1 以及具有以下值的字段的 KProperty1:要返回的ID:

import kotlin.reflect.KProperty1

fun <T> List<T>.lookup(lookup: KProperty1<T, String>, value: String, `return`: KProperty1<T, String>): String? =
  this
    .firstOrNull { lookup.get(it) == value }
    .run { if (this == null) null else `return`.get(this) }

在所有数据类中,字段和查找值始终为String 类型,因此查找函数的返回值始终为String?。

我是这样使用的:

data class Test(
  val ID: String,
  val TEXT: String
  // more fields ...
)

val list = listOf(
  Test("1", "abc"),
  Test("2", "def"),
  Test("3", "ghi"),
  Test("4", "jkl")
)

val result = list.lookup(Test::TEXT, "ghi", Test::ID)

println(result)   // Output: 3

问题是:有没有办法省略函数的两个参数中的类名?基本上是这样的:

val result = list.lookup(::TEXT, "ghi", ::ID)

它可以用字符串来完成,但当然存在拼写错误的危险:

val result = list.lookup("TEXT", "ghi", "ID")

我问的原因 - 除了好奇之外 - 是有数千个这样的查找 - 而且通常是很长的名称 - 它看起来会更好:

val ... = storageLocationWarehouseSubItemList.lookup(StorageLocationWarehouseSubItem::TEXT, "ghi", StorageLocationWarehouseSubItem::ID)

val ... = storageLocationWarehouseSubItemList.lookup(::TEXT, "ghi", ::ID)

I'm working on mass database transformations. There are a lot of data classes, some of them with relationships between each other similar to the ones in SQL databases. These relationships are done by UUIDs, not by nesting data classes.

To lookup up in a list such an ID by some value in another property I use a function called lookup, which takes the KProperty1 of the field I have the value for, and the KProperty1 of the field with the ID to be returned:

import kotlin.reflect.KProperty1

fun <T> List<T>.lookup(lookup: KProperty1<T, String>, value: String, `return`: KProperty1<T, String>): String? =
  this
    .firstOrNull { lookup.get(it) == value }
    .run { if (this == null) null else `return`.get(this) }

Both fields and the lookup value are always of type String in all data classes, and therefore the return value of the lookup function is always String?.

I use it like this:

data class Test(
  val ID: String,
  val TEXT: String
  // more fields ...
)

val list = listOf(
  Test("1", "abc"),
  Test("2", "def"),
  Test("3", "ghi"),
  Test("4", "jkl")
)

val result = list.lookup(Test::TEXT, "ghi", Test::ID)

println(result)   // Output: 3

The question is: is there a way to omit the class name in the two parameters of the function? Basically something like this:

val result = list.lookup(::TEXT, "ghi", ::ID)

It could be done with strings, but there is of course the danger of misspelling:

val result = list.lookup("TEXT", "ghi", "ID")

The reason I ask – besides being curious – is that with thousands of such lookups – and often very long names – it just would look better:

val ... = storageLocationWarehouseSubItemList.lookup(StorageLocationWarehouseSubItem::TEXT, "ghi", StorageLocationWarehouseSubItem::ID)

val ... = storageLocationWarehouseSubItemList.lookup(::TEXT, "ghi", ::ID)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

恰似旧人归 2025-01-21 08:12:52

首先,我建议不要专门要求 KProperty1 。这是一个非常具体的类型,您真正需要的只是从 TString 的映射,因此是一个函数类型:

fun <T> List<T>.lookup(lookup: (T) -> String, value: String, `return`: (T)  -> String): String? =
    this
        .firstOrNull { lookup(it) == value }
        .run { if (this == null) null else `return`(this) }

它与属性引用一起使用,就像您的原始代码一样,但它也接收函数引用和 lambda。

现在,回答你的问题:我没有看到直接执行此操作的方法,但是因为你需要经常使用它,所以提供额外的函数来使其成为可能是有意义的:

fun ID(obj: Test) = obj.ID
fun TEXT(obj: Test) = obj.TEXT

val result = list.lookup(::TEXT, "ghi", ::ID)

你可以拥有多个具有相同功能的此类函数名称,并且将使用列表的类型正确解析它们:

data class Test2(
  val ID: String,
  val TEXT: String
)

fun ID(obj: Test2) = obj.ID
fun TEXT(obj: Test2) = obj.TEXT

val result2 = listOf<Test2>().lookup(::TEXT, "ghi", ::ID)

它远非完美,但至少它有效。

First of all, I suggest not requiring KProperty1 specifically. This is a very specific type and what you really need is just a mapping from T to String, so a function type:

fun <T> List<T>.lookup(lookup: (T) -> String, value: String, `return`: (T)  -> String): String? =
    this
        .firstOrNull { lookup(it) == value }
        .run { if (this == null) null else `return`(this) }

It works with property references, as your original code, but it also receives function references and lambdas.

Now, answering your question: I don't see a way to do this directly, but because you need to use this a lot, maybe it makes sense to provide additional functions to make it possible:

fun ID(obj: Test) = obj.ID
fun TEXT(obj: Test) = obj.TEXT

val result = list.lookup(::TEXT, "ghi", ::ID)

You can have multiple such functions with the same name and they will be properly resolved using the type of the list:

data class Test2(
  val ID: String,
  val TEXT: String
)

fun ID(obj: Test2) = obj.ID
fun TEXT(obj: Test2) = obj.TEXT

val result2 = listOf<Test2>().lookup(::TEXT, "ghi", ::ID)

It is far from perfect, but at least it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文