从一个泛型接口中扩展函数。

4

考虑以下接口

interface EntityConverter<in A, out B> {
    fun A.convert(): B

    fun List<A>.convert(): List<B> = this.map { it.convert() }
}

我希望在Spring Boot应用程序中使用它,其中特定的实现被注入,使得扩展函数可以在类型上使用。

但是这样不起作用,编译器无法解析扩展函数。

2个回答

4
注意,您正在定义扩展函数,这些函数也是EntityConverter类型的成员函数。您应该查看文档的此部分,了解其工作原理。
基本上,为了使用它们,您需要在范围内拥有2个实例:
  • 调度接收器(EntityConverter<A,B>的实例)
  • 扩展接收器(AList<A>的实例,其中A与当前EntityConverter的第一个类型参数匹配)
您可以使用with()EntityConverter引入范围,以便您可以使用常规的 . 语法在其他实例上使用convert:
val converter = object : EntityConverter<Int, String> {
    override fun Int.convert() = "#$this"
}

val list = listOf(1, 2, 3)

val convertedList = with(converter) {
    list.convert()
}
println(convertedList) // prints [#1, #2, #3]

现在您需要决定这种使用模式是否最适合您的情况。如果您更喜欢没有扩展名的“传统”调用(converter.convert(a)返回一个B),则可以将您的函数声明为常规方法,以接受参数而不是接收方。
奖励:函数接口
顺便说一下,如果在EntityConverter接口前面添加fun关键字,则可以非常容易地创建它的实例,如下所示:
val converter = EntityConverter<Int, String> { "#$this" }

这是因为您的转换器接口只有一个抽象方法,使得使用单个lambda实现变得容易。请参阅有关函数式接口的文档。


1
我不确定是否可以将扩展函数作为接口的一部分进行提及,因为它们类似于静态函数。
我建议在接口中使用带有类型参数A的“通用”函数。然后就在附近放置列表的扩展方法即可。
interface EntityConverter<in A, out B> {
    fun convert(a: A): B    
}

fun <A, B> EntityConverter<A, B>.convert(list: List<A>): List<B> = list.map { convert(it) }

更新

我之前不知道在Kotlin中可以继承扩展方法,也不知道可以重写它们。所以我的答案只是使用扩展方法的另一种选择。


那么你不认为有办法将扩展函数放在 A / List<A> 上吗? - Linde_98
这不是完全正确的。扩展可以成为成员,而且不是静态的,它们可以访问封闭类的状态。这是 Kotlin 中基于上下文编程的主要工具。 - Joffrey
@Joffrey 文档 表示扩展函数“是静态分派的,这意味着它们不是按接收器类型虚拟的”。我不确定实现方式,但它们的行为就像静态函数一样。 - Ircover
抱歉如果我表达不清。常规扩展函数确实是静态分派的。然而,成员扩展只在扩展接收器方面静态分派,在调度接收器方面则是虚拟的。因此,它们可以被覆盖以关于其调度接收器。这在将扩展声明为成员中有解释(请刷新,因为链接似乎行为奇怪)。 - Joffrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接