gpt4 book ai didi

kotlin - 以允许可为空类型和使用该类型参数声明 `KClass` 的方式声明函数泛型类型参数

转载 作者:行者123 更新时间:2023-12-05 00:48:22 28 4
gpt4 key购买 nike

KClass定义为 public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier
这很棘手,因为类 String?应该是 KClass<String> ,但无法获得。

鉴于以下 3 个示例(它们都应该做本质上相同的工作),其中 1 个无法编译,而其他示例返回相同的运行时类型。

inline fun <reified T> test1(): Any = T::class
inline fun <reified T: Any> test2(): KClass<T> = T::class
inline fun <reified T> test3(): KClass<T> = T::class // does not compile

test1<String?>() // class kotlin.String
test1<String>() // class kotlin.String
test2<String?>() // does not compile
test2<String>() // class kotlin.String

问题的重点是问:我怎样才能得到 test1的运行时行为?具有 test2 的编译时行为(和安全性) ?

编辑:
该问题的最后一个附录是另一个示例,它演示了获取可空类型的类的问题。
inline fun <reified T> test4() {
val x = T::class // compiles, implied type of x is KClass<T>
val y: KClass<T> = T::class // does not compile with explicit type of KClass<T>
}

我特别遇到问题的调用站点是这样的:
class OutputContract<T>(
private val output: () -> T,
val outputType: KClass<T> // ERROR!
) {
fun invoke(): T {
return output()
}
}


inline fun <reified T> output(noinline output: () -> T): OutputContract<T> {
return OutputContract(output, T::class)
}

这里唯一的错误是 KClass<T> ,不与 T::class ,嗡嗡作响就好了。我希望允许消费者将可空性指定为契约(Contract)的一部分,因此添加了 Any约束将不起作用。如果我只是转 KClass<T>进入 KClass<Any> ,这一切都有效(这证明没有运行时问题,只有编译时间)。这最终是我选择的解决方法,但如果我能真正保持正确的类型,那就太好了。

最佳答案

您的问题缺少最重要的信息。您展示了将您的函数调用为 myFun<String?>() 的人为案例,但如果是这种情况,您显然可以将其更改为不使用可空类型。所以这可能不是真正的用例。您过于简化了您的解释并删除了我们需要回答您的问题的最相关信息:“完整的方法签名是什么,调用站点是什么样的?”

缺少的是你如何推断​​你的类型 T ?您可以从返回值、方法参数或通过在每个调用站点中显式声明它来获取它。因此,您可以选择在您的函数中使用 T: Any,并根据您未在问题中显示的信息来决定哪个最好。

所以这里有你的选择:

  • 如果根据返回参数推断类型,则允许返回可为空但不要使具体化类型为空:
    // call site, any return type nullable or not
    val something: String? = doSomething()

    // function
    inline fun <reified T: Any> doSomething(): T? {
    val x: KClass<T> = T::class
    // ...
    }
  • 或者,如果您从传入参数中推断出它,请在那里执行相同的技巧:
    // call site, any parameter type nullable or not
    val param: String? = "howdy"
    doSomethingElse(param)

    // function
    inline fun <reified T: Any> doSomethingElse(parm: T?) {
    val x: KClass<T> = T::class
    // ...
    }
  • 或者您实际上是在指定泛型参数(只是在键入参数名称时不要使其可为空):
    // call site, any non-nullable generic parameter
    doSomething<String>()

    // function
    inline fun <reified T: Any> doSomethingElse() {
    val x: KClass<T> = T::class
    // ...
    }
  • 或者如果您不能更改通用参数,则使用星形投影(但为什么不能?!?):
    // call site: whatever you want it to be

    // function:
    inline fun <reified T> test4() {
    val x = T::class // compiles, implied type of x is KClass<T>
    val y: KClass<*> = T::class KClass<T>
    }

    顺便说一下,xy 的作用相同,并且 KClass 引用中将缺少某些方法/属性。

  • 这些示例中有四分之三可以满足您的需求,我无法想象其中一个不起作用的情况。否则,您如何推断 T 类型?什么是与上述不兼容的模式?

    注意在同一方法签名中使用 <T: Any>T? 组合的技巧。

    根据您对问题的上次更新,这将按照您对 output 函数引用的预期保持可空性,但允许它适用于 KClass :
    class OutputContract<T: Any>(private val output: () -> T?, val outputType: KClass<T>) {
    fun invoke(): T? {
    return output()
    }
    }

    inline fun <reified T: Any> output(noinline output: () -> T?): OutputContract<T> {
    return OutputContract(output, T::class)
    }

    用户仍然可以通过传递不返回空值的输出实现来控制可空性,Kotlin 仍会对其进行类型检查并正常运行。但是必须检查对 invoke 的调用,因为它总是被假定为可为空的。您不能真正拥有这两种方式,想要对 T 进行可空性控制,但在内部将其用作类型化 KClass ,但您可以将其用作 KClass<*> ,具体取决于您从 KClass 使用的功能。你可能不会遗漏任何重要的东西。你没有展示你打算用 KClass 做什么,所以很难说更多关于这个话题的内容。 KClass 通常不是一个好用的类型,如果您认为它们可能会传递给您一个泛型类,您应该使用 KType 代替。

    Image from Understanding Generics and Variance in Kotlin
    图片来自 Understanding Generics and Variance in Kotlin

    关于kotlin - 以允许可为空类型和使用该类型参数声明 `KClass<T>` 的方式声明函数泛型类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49620173/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com