Как передать обобщенную приостановку лямбда-выражения методу класса в Kotlin?

Хотите иметь функцию внутри открытого класса, которая может принимать приостановленную лямбду и запускать ее.

Я знаю, что это работает, когда вы явно указываете тип, но, если возможно, он должен принимать лямбды с общей областью действия.

class ChildClass : SuperClass() {

    // does work :)
    fun launch(block: suspend ChildClass.() -> Unit) =
        coroutineThing { this.block() }

}
open class SuperClass {

    // doesn't work :(
    fun <T : SuperClass> launch(block: suspend T.() -> Unit) = 
        coroutineThing { this.block() }

}

Я получаю сообщение об ошибке Expression 'block' of type 'suspend T.() -> Unit' cannot be invoked as a function. The function 'invoke()' is not found.

Изменить:

Пытаемся в конечном итоге вызвать этот метод из экземпляра ChildClass следующим образом: ChildClass().launch { doStuff() }


person jtsalva    schedule 29.09.2019    source источник


Ответы (2)


Можно так писать, но я не знаю, как это работает :) Для меня это похоже на проблему с Котлином.

open class SuperClass {
    fun <T : SuperClass> launch(block: suspend T.() -> Unit) =
        coroutineThing { block.invoke(this as T) }
}

но в этом случае вы должны вызвать его так:

ChildClass().launch<ChildClass> { println("test") }

но вы можете немного изменить его, чтобы вызывать его как хотите:

open class SuperClass<T : SuperClass<T>> {
    fun launch(block: suspend T.() -> Unit) =
        coroutineThing { block.invoke(this as T) }
}

class ChildClass : SuperClass<ChildClass>()

//invokation
ChildClass().launch { println("test") }
person Andrei Tanana    schedule 29.09.2019
comment
Спасибо! Они оба работают для меня, я решил использовать последнее, работает лучше в моем случае использования. - person jtsalva; 29.09.2019
comment
Да, второй стандарт (см. en.wikipedia.org/wiki/ или ищите F-ограниченный полиморфизм). - person Alexey Romanov; 29.09.2019

Как насчет этого ?

open class SuperClass {


    fun<T: SuperClass> launch(superClass: T, block: suspend T.() -> Unit) {
        GlobalScope.launch {
            superClass.block()
        }
    }

}


open class ChildClass: SuperClass() {
    suspend fun print() {

    }
}


fun test () {
    val a = ChildClass()
    a.apply {
        launch(this) {
            print()
        }
    }
}
person Ninja420    schedule 29.09.2019
comment
У меня не совсем работает, так как мне нужен метод запуска внутри SuperClass. В идеале этот метод будет вызываться из экземпляров ChildClass, он предпочел бы не создавать экземпляр ChildClass просто для передачи. Конечная цель будет примерно такой: ChildClass().launch { doStuff() } :) - person jtsalva; 29.09.2019
comment
@jtsalva Я немного изменил решение, проверьте сейчас - person Ninja420; 29.09.2019
comment
Думаю, это работает, но искал что-то более чистое, выбранное для использования ответа Андрея. - person jtsalva; 29.09.2019