【问题标题】:How to specify recursive generic parameter in Kotlin?如何在 Kotlin 中指定递归泛型参数?
【发布时间】:2018-04-01 19:03:20
【问题描述】:

我只想从使用 Log4j (v2.8.2) 的 Java 移植以下代码:

ConsoleAppender appender = ConsoleAppender.newBuilder().
            withName("ConsoleAppender").build();

问题在于 newBuilder() 方法,它在 log4j 中被定义为某种递归泛型:

@PluginBuilderFactory
public static <B extends Builder<B>> B newBuilder() {
    return new Builder<B>().asBuilder();
}

Java 代码会自动推断泛型参数,而 Kotlin 则不然。在 Kotlin 中调用这个方法有什么解决方案吗?

我在 Kotlin 中尝试过的代码:

val appender = ConsoleAppender.newBuilder().withName("ConsoleAppender").build()

出现以下错误:

Error:(90, 48) Kotlin: Type inference failed: Not enough information to infer parameter B in fun !> newBuilder(): B!请明确说明。

当代码在粘贴时自动从 Java 转换时,它会设置一些存根 newBuilder&lt;B&gt;(),其中 B 未定义,我不知道它应该是什么。

【问题讨论】:

  • 能否包含您尝试过的代码和收到的错误消息
  • 在您尝试的行中,我看不出编译器应该如何推断类型。在 Java 示例中,您显式声明了类型,但在 Kotlin 中没有。
  • 您是否尝试过明确指定附加程序类型?喜欢:val appender : ConsoleAppender = ...
  • 是的,现在试了,同样的错误。

标签: generics kotlin log4j


【解决方案1】:

Log4J 似乎使用了构建器模式,在 Kotlin 中要求每个开放的非抽象类都有两个构建器:一个是通用的,可以被子类扩展,另一个是非通用的,可以被实例化。

由于您可能不想修改 Log4J,我会考虑使用反射:

fun main(args: Array<String>) {
    val builder = ConsoleAppender::class.java.getMethod("newBuilder").invoke(null) as ConsoleAppender.Builder<*>
    val appender = builder.withName("ConsoleAppender").build()
    println(appender.name)
}

discuss.kotlinlang.org查看我的回答:

Recursive generic in Builder pattern

【讨论】:

  • 暂时使用此解决方案。
【解决方案2】:

我发现等待 log4j2 配置如何解决这个问题,请参阅示例:

// create concrete type just to call method
// however, we should not use it, because of possible runtime casting issues
private class ConsoleBuilder : ConsoleAppender.Builder<ConsoleBuilder>()

private fun createAppender(configuration: Configuration): ConsoleAppender {
    // hide our temporary class immediately, to avoid runtime type casting issue
    return (ConsoleAppender.newBuilder<ConsoleBuilder>() as ConsoleAppender.Builder<ConsoleBuilder>).apply {
        withName("MyAppender")
        setConfiguration(configuration)
    }.build()
}

【讨论】:

  • 我检查了这个解决方案,它有效。但是我对这种带有类型转换异常的hack表示怀疑,如果将转换的表达式分配给构建器变量,则会引发该异常。这个编译器实现是否依赖?是否保证可以在未来的版本上工作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多