21

下面我的扩展功能有什么问题

class Foo<T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

更新

我想知道为什么它与常规扩展函数不同,其中 T 成功地被推断为Any并希望实现相同的行为,例如 T 被推断为 Foo<Any>

class Foo {
    fun <T> T.foo(that: T): T = throw Exception()

    init {
        "str" foo 42
    }
}
4

3 回答 3

34

这个问题是泛型如何工作的核心。

class Foo {
    fun <T> T.foo(that: T): T = throw Exception()

    init {
        "str" foo 42
    }
}

这是可行的,因为编译器可以找到T同时适合函数签名和参数的 a:它是Any,并且函数变成了这个:

fun Any.foo(that: Any): Any = ...

现在,String是 的子类型AnyInt是 的子类型Any,所以这个函数适用于参数。

但在你的第一个例子中:

class Foo<T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

一切都不一样了。没有这样的T。让我们天真地尝试一下Any

fun Foo<Any>.plus(that: Foo<Any>): Foo<Any> = ...

现在,在 中Foo不变T,所以Foo<Int>不是的子类型,事实上,除此之外没有其他类型成为的超类型。所以,必须是exact ,但也必须是完全一样的逻辑(因为第二个参数),所以没有解决方案,函数不适用。Foo<Any>TIntFoo<T>Foo<Int>TIntString

Foo 您可以通过在以下中进行协变来使其工作T

class Foo<out T> {
    fun <T> Foo<T>.plus(that: Foo<T>): Foo<T> = throw Exception()

    init {
        Foo<Int>() + Foo<String>()  // A receiver of type Foo<T> is required
    }
}

这对 的成员的可能签名施加了一些限制Foo,但如果您对他们没问题,它可以解决您的问题。

查看此链接了解更多详细信息:http: //kotlinlang.org/docs/reference/generics.html

于 2015-10-01T16:49:38.280 回答
6

我认为 Andrey Breslaw 接受的答案是正确的,但提供了不正确的解决方案。

只需要告诉编译器为提供的泛型类型参数推断公共超类型,即,只要 Foo 的泛型类型参数共享一个公共超类型(并且它们总是会),使用它。喜欢:

operator fun <T, R: T, S: T> Foo<R>.plus(that: Foo<S>): Foo<T> = throw Exception()

现在,如果类型不匹配,返回的 Foo 的结果泛型类型参数将根据需要扩大,但操作本身是合法的,不会引入协变。

于 2019-03-23T09:30:59.250 回答
2

您的方法plus期望参数具有与T接收器相同的泛型类型参数。因此,您不能将 a 添加Foo<String>到 a Foo<Int>

如果您希望能够添加所有类型的Foo,那么您需要像这样声明您的扩展函数:

operator fun <T,R> Foo<T>.plus(that: Foo<R>): Foo<T> = throw Exception()
于 2015-10-01T14:43:13.103 回答