Kotlin-通用类型参数不受尊重 [英] Kotlin - Generic Type Parameters Not Being Respected

查看:220
本文介绍了Kotlin-通用类型参数不受尊重的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例:

import kotlin.reflect.KProperty1

infix fun <T, R> KProperty1<T, R>.test(value: R) = Unit

data class Foo(val bar: Int)

fun main() {
    Foo::bar test "Hello"
}

鉴于test期望类型为Rvalue,为什么在这种情况下,如果属性类型为Int,它是否允许我传递String?

Given that test expects a value of type R, why in this context, where the property type is Int, does it allow me to pass a String?

推荐答案

首先,查看接口KProperty1的声明,该声明为:

First, take a look at the declaration of the interface KProperty1, which is:

interface KProperty1<T, out R> : KProperty<R>, (T) -> R

这里的重要部分是 out-投影的类型参数R,它定义了KProperty1类型之间的子类型关系,并为R使用了不同的类型参数.

The important part here is the out-projected type parameter R, which defines subptyping relationships between the KProperty1 types with different type arguments used for R.

(1) 即,对于任何FooAB,使得A : B(A),
KProperty1<Foo, A> : KProperty1<Foo, B>.之所以称为协方差,是因为参数化类型彼此之间的关联方式与其类型参数相同.

(1) Namely, for any Foo, A and B such that A : B (A is a subtype of B),
KProperty1<Foo, A> : KProperty1<Foo, B>. This is called covariance, because the parameterized types relate to each other in the same way as their type arguments do.

(2) 接下来,请注意,对于任何AB,使得A : B可以将A的实例作为任何B类型参数的参数.扩展功能的接收器参数在这方面与普通参数没有什么不同.

(2) Next, note that for any A and B such that A : B, an instance of A can be passed as an argument to any B-typed parameter. Receiver parameters of extension functions are not different from normal parameters in this respect.

现在,关键部分是编译器运行的类型推断算法.类型推断的目标之一是为每个泛型调用建立静态已知的类型实参,而忽略类型实参.

Now, the crucual part is the type inference algorithm that the compiler runs. One of the goals of type inference is to establish statically-known type arguments for each generic call where type arguments are omitted.

在调用Foo::bar test "Hello"的类型推断期间,编译器需要根据接收方Foo::bar(KProperty1<Foo, Int>)和T和R的类型参数. >参数"Hello"(String).

During type inference for the call Foo::bar test "Hello", the compiler needs to actually infer the type arguments for T and R based on the known types of the receiver Foo::bar (KProperty1<Foo, Int>) and the value argument "Hello" (String).

这是通过解决约束系统在内部完成的.我们可以如下模拟此逻辑:

This is done internally by solving a constraint system. We could emulate this logic as follows:

  • 鉴于KProperty<Foo, Int>作为KProperty<T, R>传递:

  • 我们必须使用T := Foo(因为T是不变的)
  • 我们必须使用Int或其任何超类型作为类型参数R
    • 这是因为R的协方差:给定 (1) (2) ,必须为R选择Int或它的某些超类型才能在期望KProperty<Foo, R>的地方传递KProperty<Foo, Int>
    • 这些超类型的示例是Int?NumberNumber?AnyAny?
    • we must use T := Foo (as T is invariant)
    • we must use Int or any of its supertypes as the type argument R
      • this is because of covariance for R: given (1) and (2) combined, choosing Int or some of its supertypes for R is necessary to be able to pass KProperty<Foo, Int> where KProperty<Foo, R> is expected
      • examples of these supertypes are Int?, Number, Number?, Any, Any?

      鉴于String作为R传递:

      • 我们必须使用String或其某些超类型作为R
        • 这是能够通过String的必要条件,其中由于 (2)
        • 这些超类型的示例是String?CharSequenceCharSequence?AnyAny?
        • we must use String or some of its supertypes as R
          • this is necessary to be able to pass a String where R is expected due to (2)
          • examples of these supertypes are String?, CharSequence, CharSequence?, Any, Any?

          鉴于R的两个约束,即它应该是Int或其某些超类型,并且它应该是String或其某些超型,编译器会找到同时满足这两个条件的最不常见的类型.此类型为Any.

          Given the two constraints on R, namely that it should be Int or some of its supertypes and it should be String or some of its supertypes, the compiler finds the least common type that satisfies both. This type is Any.

          因此,推断出的类型参数是T := FooR := Any,使用显式类型参数的调用将是:

          So, the inferred type arguments are T := Foo and R := Any, and the call with explicit type arguments would be:

          Foo::bar.test<Foo, Any>("Hello")
          

          在IntelliJ IDEA中,您可以在非插入调用上使用操作添加显式类型参数来添加推断的类型.

          In IntelliJ IDEA, you can use an action Add explicit type arguments on a non-infix call to add the inferred types.

          免责声明:这并不完全是编译器内部工作的方式,但是使用这种推理方式,您可能经常会得到与编译器结果一致的结果.

          也相关:

          这篇关于Kotlin-通用类型参数不受尊重的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆