Всем привет! Может тут кто подскажет. Есть Java би...
# russian
s
Всем привет! Может тут кто подскажет. Есть Java библиотека, в которой есть функциональный интерфейс
Copy code
@FunctionalInterface
public interface ServiceCall<Request, Response> {
  CompletionStage<Response> invoke(Request request);
}
В принципе нет никакой проблемы использовать это и в Kotlin, но всё-таки это не Kotlin way, где есть корутины и
suspend
функции. Вопрос: Могу ли я разово написать какой-то Kotlin extension к этой библиотеке, в котором будет свой
KServiceCall
с методом
Copy code
suspend fun invoke(request: Request ): Response;
И чтобы везде, где на вход требуется оригинальный
ServiceCall
, я передавал уже свой
KServiceCall
?
m
runBlocking
? 😀
😂 1
b
если
ServiceCall
- java интерфейс то можно сделать так
Copy code
// класс который нужно расширить
class Foo { 
    fun <Request, Response> async(f: ServiceCall<Request, Response>) {}
}

fun <Request, Response> Foo.async(f: suspend (Request) -> Response) {
    // GlobalScope для примера
    async(ServiceCall<Request, Response> { GlobalScope.async { f(it) }.asCompletableFuture() })
}

fun main() {
    val foo = Foo()
    foo.async<String, Int> { it.toInt() }
}
если ServiceCall - котлиновский интерйейс тот:
Copy code
fun <Request, Response> Foo.async(f: suspend (Request) -> Response) {
    val callBack = object : ServiceCall<Request, Response> {
        override fun invoke(request: Request): CompletionStage<Response> {
            return GlobalScope.async { f(request) }.asCompletableFuture()
        }
    }
    async(callBack)
}
но стоит дважды подумать нужно ли вам это
s
А без GlobalScope можно?
b
другой скоуп сделать - свой
s
А свой обязательно? Готового простенького билдера нет на этот случай?
g
CoroutineScope(SomeContext) - готовый билдер, только это имеет смысл если вы его будете в какой то момент отменять, т.е. управлять жизненным циклом, иначе смысла нет и проще GlobalScope
k
"не Kotlin way" - плохая причина писать костыли. Подумай о людях которые после тебя в твоих враперах будут разбираться.
s
@gildor Если я на обработку каждого входящего http запроса буду делать
GlobalScope.future
это нормально? Я возможно не так понял документацию, но у меня сложилось ощущение, что каждая корутина из GlobalScope осядет в хипе и умрёт только вместе с приложение. Из чего я делаю вывод, что если я на каждый HTTP запрос будут дергать
GlobalScope.future
, то OOM мне обеспечен быстрее, чем я смогу что-либо понять 🙂 В реальности нужна корутина (со всеми её чилдами) только на обработку HTTP запроса и хотелось бы, чтобы её жизненный цикл закончился вместе с жизненным циклом запроса (=по завершению метода, в котором корутина родилась)
@Konstantin Petrukhnov Так идея то не костыль написать, а наоборот людям облегчить работу. Чтобы им не нужно было руками каждый раз родительскую корутину самим создавать, чтобы можно было suspend функции повызывать. Есть в Java API метод, возвращающий CompletableFuture. Хочется один раз сделать так, чтобы вместо этого метода, был
suspend
метод, из которого пользователь может без проблем вызывать другие
suspend
методы и не париться.
g
Да, вы не верно поняли документацию. Корутина не зависнет, она будет собрана когда запрос завершится или будет отменён Проблема GlobalScope в том что запущенные в нем корутина ни к чему не привязаны, их не отменить отменой скоупа
Лучше конечно иметь специальный скоуп со своим жизненным циклом, но если просто создать скоуп и никогда его не отменять это ничем не отличается от GlobalScope
Но все это не имеет никакого отношения к изначальному вопросу как мне кажется. Судя по оригинальному интерфейсу не понятно зачем нужны врапперы, там же возвращается CompletionStage и на него уже есть адаптер, просто вызвать await()
s
@gildor
Да, вы …
К чёрту формальности, лучше на ты 😄
Лучше конечно иметь специальный скоуп со своим жизненным циклом, но если просто создать скоуп и никогда его не отменять это ничем не отличается от GlobalScope
Спасибо за комментарий! 👍 Я почему-то после прочтения был уверен, что контекст корутины из хипа не пропадёт, так как на него будет ссылка из синглтона GlobalScope, жизненный цикл которого равен жизненному циклу приложения.
Судя по оригинальному интерфейсу не понятно зачем нужны врапперы, там же возвращается CompletionStage и на него уже есть адаптер, просто вызвать await()
Возможно не совсем корректно сформулировал. Дело в том, что нам нужно не вызывать этот метод, а реализовывать. Т.е. по коду разбросаны десятки анонимных имплементаций этого интерфейса (в виде лямбд конечно). И в этих имплементациях классические вермишели из thenApply.thenApply.thenCombile и тому подобное. Хочется всё это переписать на корутины. При этом так, чтобы на месте текущих Java лямбд
Copy code
return request -> {
    // this return CompletableFuture
}
появилось что-то вроде
Copy code
return serviceCall {
    // this I can call suspend functions
}