如何从通用代码调用扩展函数

时间:2018-05-07 08:45:02

标签: kotlin

给出以下类和扩展函数

class Foo 
class Bar

fun Foo.run() = "Foo.run"
fun Bar.run() = "Bar.run"

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map({a -> a.run()}) //compiler error
    println("Hello, world!")
}

是否可以通用方式调用run

不知何故,这是尝试通过界面扩展类时模拟Swift的protocolextension,或者不是可能或不可取

3 个答案:

答案 0 :(得分:1)

您只需要一个通用类型并扩展此类型。一个简单的标记界面将有助于此:

interface Common
class Foo: Common
class Bar: Common

fun Common.run() = "run"

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map {a -> a.run() }
}

sealed(正如您在OP中所建议的那样):

sealed class Common {
    class Foo: Common()
    class Bar: Common()
}

fun <T: Common> T.run() = "run"

fun main(args: Array<String>) {
    val x = listOf(Common.Foo(), Common.Bar())
    val y = x.map {a -> a.run() }
}

答案 1 :(得分:1)

您的列表包含FooBar - Any的最低常见类型,您应该在调用run方法之前检查lambda中的类型:< / p>

class Foo
class Bar

fun Foo.run() = "Foo.run"
fun Bar.run() = "Bar.run"

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map({ a ->
        when (a) {
            is Foo -> a.run()
            is Bar -> a.run()
            else -> {
                /* ignore */
            }
        }
    })
    println("Hello, world!")
}

class Foo
class Bar

fun Foo.run() = "Foo.run"
fun Bar.run() = "Bar.run"

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map({ a ->(a as? Foo)?.run() ?: (a as? Bar)?.run() })
    println("Hello, world!")
}

由于x中推断的元素类型为Any,因此它可以包含FooBar以外的元素,因此您的代码无法找到这种元素的方法,这在编译时检查。

即使您使用reified type check添加扩展功能,它也会转换为最低公共父级:

class Foo
class Bar

fun Foo.run() = "Foo.run"
fun Bar.run() = "Bar.run"
fun Any.run() = "Any.run" // this is type erasured version of 'inline fun <reified T> T.run() = T::class.simpleName+".run"'

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map(Any::run)
    println(y)
}

将打印[Any.run, Any.run]

但是使用this的引用,您可以为您的函数提供数据,如:

class Foo 
class Bar 

inline fun Any.run() = this::class.simpleName + ".run"

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar())
    val y = x.map(Any::run)
    println(y)
}

将打印[Foo.run, Bar.run]

您还可以将代码调度子类类型移动到这样的扩展函数:

class Foo
class Bar

fun Foo.run() = "Foo!!!.run"
fun Bar.run() = "Bar!!!.run"
fun Any.run() = when(this){
    is Foo -> run()
    is Bar -> run()
    else -> { "Unknown.run" }
}

fun main(args: Array<String>) {
    val x = listOf(Foo(), Bar(), Any())
    val y = x.map({ a -> a.run()
    })
    println(y)
}

将打印[Foo!!!.run, Bar!!!.run, Unknown.run]

答案 2 :(得分:0)

你可以这样做。

fun <T> T.run() = "run"

这个run定义应该使代码的其余部分编译。

如果您想限制run的被访问者,您应该像这样创建一个公共sealed class

class Foo : Bla()
class Bar : Bla()
sealed class Bla

fun <T : Bla>.run() = "run"

// this works, too
// fun Bla.run() = "run"

通过这种方式,只有FooBar的实例可以使用run

但如果我是你,我会run成为Bla的成员函数。