Передача лямбда вместо интерфейса
Я создал интерфейс:
interface ProgressListener {
fun transferred(bytesUploaded: Long)
}
но может использовать его только как анонимный класс, а не лямбда
dataManager.createAndSubmitSendIt(title, message,
object : ProgressListener {
override fun transferred(bytesUploaded: Long) {
System.out.println(bytesUploaded.toString())
}
})
Я думаю, что это должна быть возможность заменить его лямбдой:
dataManager.createAndSubmitSendIt(title, message, {System.out.println(it.toString())})
Но я получаю ошибку: Тип несоответствие; требуется - ProgressListener, найдено -() → Единица?
Что я делаю не так?
Ответы
Ответ 1
Как сказал @zsmb13, конверсии SAM поддерживаются только для интерфейсов Java.
Вы могли бы создать функцию расширения, чтобы заставить ее работать:
// Assuming the type of dataManager is DataManager.
fun DataManager.createAndSubmitSendIt(title: String,
message: String,
progressListener: (Long) -> Unit) {
createAndSubmitSendIt(title, message,
object : ProgressListener {
override fun transferred(bytesUploaded: Long) {
progressListener(bytesUploaded)
}
})
}
Ответ 2
Kotlin поддерживает только конверсии SAM для интерфейсов Java.
... отметим, что эта функция работает только для Java interop; поскольку Kotlin имеет собственные типы функций, автоматическое преобразование функций в реализации интерфейсов Kotlin не является необходимым и поэтому не поддерживается.
- Официальная документация
Если вы хотите использовать лямбда в параметре, сделайте свою функцию вместо параметра "параметр функции" вместо параметра. (На данный момент, по крайней мере. Поддержка конверсий SAM для интерфейсов Kotlin - постоянное обсуждение, это была одна из возможных будущих возможностей в прямом эфире Kotlin 1.1).
Ответ 3
Немного поздно для вечеринки: вместо создания интерфейса вы позволяете компилятору создавать его, беря функцию напрямую, а не интерфейс в вашем datamanager, например:
fun createAndSubmitSendIt(title: String, message: String, transferred: (Long) -> Unit) {
val answer = TODO("whatever you need to do")
transferred(answer)
}
и тогда вы просто используете его, как вы этого хотите! Если я правильно помню, что компилятор kotlin/jvm делает то же самое, что и сделать интерфейс.
Надеюсь, поможет!
Ответ 4
Другим решением было бы объявить typealias, ввести его куда-нибудь и вызвать его. Вот пример:
internal typealias WhateverListener = (String) -> Unit
и затем мы вводим эти typealias в наш класс:
class Gallery constructor(private val whateverListener: WhateverListener) {
...
galleryItemClickListener.invoke("hello")
...
}
итак у нас наша лямбда
val gallery = Gallery { appNavigator.openVideoPlayer(it) }
Благодарности моему коллеге Джоэлю Педрасе, который показал мне хитрость, пытаясь найти решение <3.
Ответ 5
Единственного окончательного решения этой проблемы не существует, если вы стремитесь получить лучший доступ как с Kotlin, так и с Java.
Если бы разработчики Kotlin не думали, что преобразование SAM для интерфейсов Kotlin не является необходимым, метод "Kotlin Interface" был бы окончательным решением.
https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Также обратите внимание, что эта функция работает только для взаимодействия с Java; с котлина имеет надлежащие типы функций, автоматическое преобразование функций в реализация интерфейсов Kotlin не нужна и, следовательно, неподдерживаемый.
Выберите лучшее решение для вашего варианта использования.
Тип функции Котлина
-
API Kotlin: отлично
- Kotlin Access: отлично
- Java Access:
- Автоматически сгенерированный тип параметра, такой как Function1 (не большая проблема для лямбды Java 8)
- Подробный
return Unit.INSTANCE;
вместо пустого возврата.
class KotlinApi {
fun demo(listener: (response: String) -> Unit) {
listener("response")
}
}
fun kotlinConsumer() {
KotlinApi().demo { success ->
println(success)
}
}
public void javaConsumer() {
new KotlinApi().demo(s -> {
System.out.println(s);
return Unit.INSTANCE;
});
}
Интерфейс Kotlin
-
API Kotlin: дополнительное определение интерфейса.
- Kotlin Access: слишком многословно
- Java Access: отлично
class KotlinApi {
interface Listener {
fun onResponse(response: String)
}
fun demo(listener: Listener) {
listener.onResponse("response")
}
}
fun kotlinConsumer() {
KotlinApi().demo(object : KotlinApi.Listener {
override fun onResponse(response: String) {
println(response)
}
})
}
//If Kotlin had supported SAM conversion for Kotlin interfaces. :(
//fun kotlinConsumer() {
// KotlinApi().demo {
// println(it)
// }
//}
public void javaConsumer() {
new KotlinApi().demo(s -> {
System.out.println(s);
});
}
Java-интерфейс
-
API Kotlin: смешанный Java-код.
- Kotlin Access: немного многословно
- Java Access: отлично
class KotlinApi {
fun demo(listener: Listener) {
listener.onResponse("response")
}
}
public interface Listener {
void onResponse(String response);
}
//Semi SAM conversion
fun kotlinConsumer() {
KotlinApi().demo(Listener {
println(it)
})
}
public void javaConsumer() {
new KotlinApi().demo(s -> {
System.out.println(s);
});
}
Несколько методов
-
API Kotlin: множественные реализации методов
- Kotlin Access: Отлично, если используется правильный метод. Автозаполнение также предлагает подробный метод.
- Доступ к Java: отлично. Автозаполнение не предлагает метод типа функции из-за аннотации
JvmSynthetic
class KotlinApi {
interface Listener {
fun onResponse(response: String)
}
fun demo(listener: Listener) {
demo {
listener.onResponse(it)
}
}
@JvmSynthetic //Prevents JVM to use this method
fun demo(listener: (String) -> Unit) {
listener("response")
}
}
fun kotlinConsumer() {
KotlinApi().demo {
println(it)
}
}
public void javaConsumer() {
new KotlinApi().demo(s -> {
System.out.println(s);
});
}
Java API
-
API Kotlin: API Kotlin отсутствует, весь код API - это Java
- Доступ к котлину: идеальный
- Доступ к Java: идеальный
public class JavaApi {
public void demo(Listener listener) {
listener.onResponse("response");
}
public interface Listener {
void onResponse(String response);
}
}
//Full SAM conversion
fun kotlinConsumer() {
JavaApi().demo {
println(it)
}
}
public void javaConsumer() {
new JavaApi().demo(s -> {
System.out.println(s);
});
}