【问题标题】:Kotlin: Generic function as return type?Kotlin:通用函数作为返回类型?
【发布时间】:2017-10-23 08:34:18
【问题描述】:

在 Kotlin 中,是否可以将泛型函数类型声明为函数的返回类型?

我想要在 Java 中实现的效果如下所示:

interface Factory {

    static Factory INSTANCE = new FactoryImpl();

    <T> T create(String name, Class<T> type);

}

class PrefixedFactory implements Factory {
    private final String prefix;

    PrefixedFactory(String prefix) {
        this.prefix = prefix;
    }

    @Override
    public <T> T create(String name, Class<T> type) {
        return Factory.INSTANCE.create(prefix + name, type);
    }
}

(请注意,在示例中,我使用静态字段访问 Factory 实例以避免将泛型函数作为参数传递,这会在 Kotlin 中出现其自身的问题)。 我想将前缀转换为 kotlin 函数,但似乎不可能将泛型函数声明为返回类型:

fun prefixer(prefix: String): <T> (String, KClass<T>) -> T { TODO() }

这当然不能编译。在我看来,与 Java 的功能接口相比,这是一个限制。有没有办法做到这一点,或者解决方法?

(编辑)澄清

我希望实际结果函数是通用的。如果我这样做了

fun <T: Any> prefixer(prefix: String): (String, KClass<T>) -> T { TODO() }

正如当前答案所暗示的那样;我没有得到通用函数,而是在调用prefixer&lt;Foo&gt;("") 时得到(String, KClass&lt;Foo&gt;) -&gt; Foo。所以这个函数只能用Foo调用,而工厂函数prefixer在这种情况下是通用的,结果不是。希望能解开误会。

我的用例是在一个 Gradle 插件中,我在其中编写了一个与此类似的辅助方法,它将一些默认值应用于创建的每个任务:

val myPrefix = "..."
val project: Project = <from context>
fun <T: Task> String.task(type: KClass<T>, doConfig: T.() -> Unit) {
  project.tasks.create("$prefix$this", type.java, { it.doConfig() })
}

请注意,该项目以闭包形式出现。现在我想在不同的插件中重用那个帮助器,所以我想为不同的项目实例使用工厂创建这个函数。

【问题讨论】:

    标签: function generics lambda kotlin


    【解决方案1】:

    你做的几乎是正确的。只需在prefixer函数中直接定义泛型部分即可。

    fun <T: Any> prefixer(prefix: String): (String, KClass<T>) -> T { TODO() }
    

    根据您的实际实现,您可以查看reified 关键字。

    【讨论】:

    • 与@RobCo 的答案相同的问题在这里适用-我基本上想返回一个等效于fun &lt;T: Any&gt; (String, KClass&lt;T&gt;) -&gt; T 的函数,但我不能将其指定为返回类型。
    • 如何实现这样的通用函数?您必须将其存储在包含非通用信息的变量中。无论如何,您都必须从那里投射它。也许扩展一点你真正想要实现的目标。
    【解决方案2】:

    以下行编译:

    fun <T : Any> prefixer(prefix: String): (String, KClass<T>) -> T = TODO()
    

    首先,通用减速应该紧跟在 fun 关键字之后。

    那么它必须被声明为Any类型。默认为任何?但是 KClass 只需要 Any。

    【讨论】:

    • 但是返回的函数不是通用的,它被固定为 T 在调用 prefixer 时的任何值。
    【解决方案3】:

    虽然我对 @Alexey 的回答感到失望,但我发现了一种利用 Kotlin 运算符的更简化的解决方法。以下使其在使用时看起来更像一个 lambda:

    private class Prefixer(private val: String) {
        operator fun <T> invoke(name: String, type: Class<T>): T {
            TODO()
        }
    }
    

    使用它:

    val createMy = Prefixer("MyPrefix")
    val result = createMy("Configuration", Configuration::class.java)
    

    如有必要,请随时用KClass 替换。我实际上将它用于稍微不同的目的。

    【讨论】:

      【解决方案4】:

      不,这是不可能的(据我所知)。这种类型的技术术语是“高级类型”,很少有语言支持它们,在 JVM 上我只知道 Scala。

      如果有人问我同样的问题,但没有像 Factory 这样的界面,我建议完全创建这个界面作为解决方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-19
        • 1970-01-01
        • 2020-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多