基于另一个类型参数的干扰类型参数

Interfering type parameter based on another type parameter

Kotlin 编译器是否可以根据另一个类型参数推断类型参数? 即:根据IntExampleclass定义中的类型B推断类型A。

interface Something<A>

interface Example<A, B: Something<A>>

class IntSomething: Something<Int>

// Can Int type parameter be infered by the fact that IntSomething is Something<Int>?
// If yes, how could I specify B type parameter without specifing the infered A (Int) parameter?
class IntExample: Example<Int, IntSomething> // specifing Int here is painful!!!

想象一下,我们有更多这样的类型参数 - 如果可以(理论上)推断出其中的每一个,将会有很多样板来指定它们。

编辑

经过@KarstenGabriel 的详尽回复后,我将扩展前面的示例,以明确此处使用的类型参数的用途:

interface Something<A> {
   val sth: A
}

interface Example<A, B: Something<A>> {
   val eg: A
   val something: B  
}

data class IntSomething(override val sth: Int): Something<Int>

data class DescribedIntSomething(
    override val sth: Int, 
    val description: String
): Something<Int>

data class DescribedIntExample(
   override val eg: Int,
   override val something: DescribedIntSomething,
): Example<Int, DescribedIntSomething> // specifing Int here is painful

fun main() {
  val describedIntExample = DescribedIntExample(
    eg = 1,
    something = DescribedIntSomething(1, "Just one")
  )
  
  // We have to know that something is DescribedIntSomething to read description.
  // Neither `*` nor `Something<Int>` is sufficient, we need `B: Something<Int>` to keep B
  val description = describedIntExample.something.description 
  println(description)
}

所以我们使用类型参数 - AB 作为 egsomething

return
  1. 不能使用通配符 *,因为它只是表示 Any?。我们需要保留具体类型 B。 (例如,在示例中启用阅读说明)
  2. 不能使用通配符 Something<Int> 代替 B: Something<Int>,原因与第 1 点相同

可能只是语言设计层面的问题。类型参数确实存在于编译时(它们稍后会被删除),所以 理论上 Example<DescribedIntSomething> 可能就足够了,而不是 Example<Int, DescribedIntSomething> 因为 DescribedIntSomethingSomething<Int>

这个问题在某些假设下是可以解决的:

  1. 只有 属性 eg(或其他类似情况)才需要类型参数 A
  2. 我们可以将 eg 转换为函数而不是 属性。
  3. eg(您的 A 值)的期望值可以从 something 的值(您的 B 值)导出。如果未推断值,则无法推断类型。

然后我们定义 Example 不带参数 A:

interface Example<B: Something<*>> {
    val something: B
}

现在在 Example 中我们不可能获得 BSomething 类型的类型参数,因为类型参数 ASomething<A> 在运行时不存在。所以你不能只询问 B 的实例它的 Something 类型参数是什么。而且 Kotlin 也不能这样做,因为如果您没有明确指定,信息就不会存在。

但是我们知道编译时的参数可以用在带有具体化参数的内联扩展函数中:

inline fun <reified A, reified B : Something<A>> Example<B>.eg(): A = something.sth

(文档:Inline functions with reified parameter and Extension functions

在此示例中,我假设 eg 的值应为 sth

如果想让eg是私有的,可以把函数放在Example里面,然后设为私有,否则必须在class.[=35之外指定=]

现在你可以只定义你的子class而不需要指定以前的A参数,但你仍然有正确的类型:

data class DescribedIntExample(
    override val something: DescribedIntSomething,
) : Example<DescribedIntSomething>

fun main() {
    val describedIntExample = DescribedIntExample(
        something = DescribedIntSomething(1, "Just one")
    )
    val x = describedIntExample.eg() // x has inferred type Int and value 1
}