如何使用反射动态转换数组以匹配函数的参数?

How can I dynamically cast an array to match a function's arguments using reflection?

我正在做一个项目,我想在其中解析一些输入并用它来调用一个函数。输入是运行时处理的字符串,调用的函数由开发者指定。标记化的输入将传递给指定的函数。

这演示了如何使用代码。您传入一个函数、要解析的内容和一组可以匹配以生成令牌的 tokenPatterns。

fun main(args : Array<String>) {

    val f = Funcs()

    val myColor: String = f.whatColor(Color.GREEN) // Expected
    val theirColor: String = Processor.process(f::whatColor, "green", Color.values().toSet())

    println(myColor == theirColor) // Should be true
}

class Funcs {
    fun whatColor(color: Color): String {
        return when (color) {
            Color.RED -> "nice red"
            Color.GREEN -> "cool green"
        }
    }
}

enum class Color(override val pattern: Regex) : TokenPattern {
    RED(Regex("red")),
    GREEN(Regex("green"))
}

下面的代码显示了它当前如何工作(或不能工作)的基本版本。本质上,我根据 tokenPatterns 集对输入进行标记,然后使用这些标记调用指定的函数。问题是我需要将它们转换回模式的原始实现(在本例中为 Color 枚举),以便我可以用它们调用原始函数。

/**
 * Extend to define custom tokens.
 */
interface TokenPattern {
    val pattern: Regex
}

class Token<T: Any>(val type: KClass<out T>)

/**
 * Tokenizes and runs functions
 */
object Processor {

fun process(func: KFunction<String>, input: String, tokenPatterns: Set<TokenPattern>): String {
    val tokens = tokenize(input, tokenPatterns)
    return runIt(func, tokens)
}

private fun tokenize(input: String, tokenPatterns: Set<TokenPattern>): List<Token<*>> {
    return tokenPatterns.filter { it.pattern.matches(input) }.map { Token(it::class) }
}

private fun runIt(func: KFunction<String>, tokens: List<Token<*>>): String {
    val args: Array<Any> = tokens.toTypedArray()
    // Some casting needs to be done
    return func.call(*args)
}

实际代码比这复杂,但这或多或少说明了它是如何协同工作的。我遇到的主要问题是我不确定如何动态转换回 Color 实现,以便我可以使用反射将它传递给函数。我也愿意接受其他方式来完成这项任务,任何帮助将不胜感激!谢谢!

我相信这可以通过将底层实现枚举存储或检索到令牌本身来实现。在这一行中,存储了 class,但我不确定它是指任何颜色还是特定的 Color.Red 或 Color.Green.

tokenPatterns.filter { it.pattern.matches(input) }.map { Token(it::class) }

这可能是这样恢复的,但我不确定具体怎么写。

val castedArgs: Array<Any> = tokens.map { it.type.cast(it) }.toTypedArray()

也许我可以用某种方式重新创建枚举而不是强制转换?

只需将 tokenPattern 存储到令牌中,将这些 tokenPattern 作为参数传递,反射将能够适当地转换它。

你可以这样制作令牌:

class Token(val original: TokenPattern)

然后像这样调用函数:

private fun runIt(func: KFunction<String>, tokens: List<Token>): String {
    val args: Array<Any> = tokens.map { it.original }.toTypedArray()
    return func.call(*args)
}