основы
Ниже представлен функциональный интерфейс, определенный в Java:
Джава
1
// Standard Java interface
2
public interface Executor {
3
void execute(Runnable command);
4
}
Обратите внимание, что интерфейс не должен быть помечен
@FunctionalInterface
как один.
В Kotlin это может быть реализовано с помощью следующего кода:
Котлин
xxxxxxxxxx
1
executor.execute { println("I am a runnable") }
Поскольку Executor
интерфейс имеет только одну функцию с одним входным параметром, он может назначать тип для лямбда-выражения, передаваемого в execute
. Это устраняет необходимость явно определять его как Runnable
. Это известно как SAM ( S Ingle ВТОРЕФЕРАТ М преобразования енит), см документации Kotlin для получения дополнительной информации.
Более подробный способ достижения той же цели выглядит так:
Котлин
xxxxxxxxxx
1
executor.execute(Runnable { println("I am a runnable") })
Если вы используете Intellij, он поможет вам найти первое решение.
Генерики одного типа
Давайте сделаем это немного более захватывающим и добавим немного дженериков на этот раз. Взяв Java-интерфейс и функцию для его вызова:
Джава
xxxxxxxxxx
1
2
public interface MyJavaInterfaceWithGenerics<A> {
3
A execute();
4
}
5
public static <A> A doStuff(MyJavaInterfaceWithGenerics<A> myJavaInterface) {
7
return myJavaInterface.execute();
8
}
doStuff
Функция может быть вызвана следующим кодом Котлин:
Котлин
xxxxxxxxxx
1
// Least simplified
2
doStuff(MyJavaInterfaceWithGenerics<String> { "hi" })
3
// A bit more simplified
4
doStuff(MyJavaInterfaceWithGenerics { "hi" })
5
// Specify generic type explicitly
6
doStuff<String> { "hi" }
7
// Let Kotlin do the work
8
doStuff { "hi" }
Тип возврата наиболее упрощенного кода определяется результатом лямбда, который в этом случае является String
.
Multi-Type Generics
Как насчет того, когда генерики становятся немного сложнее? Я немного оживил пример кода, чтобы продемонстрировать это:
Джава
xxxxxxxxxx
1
2
public interface MyJavaInterfaceWithGenerics<A, B, C> {
3
C execute(A a, B b);
4
}
5
public static <A, B, C> C doStuff(A a, B b, MyJavaInterfaceWithGenerics<A, B, C> myJavaInterface) {
7
return myJavaInterface.execute(a, b);
8
}
На самом деле у меня возникли некоторые затруднения, когда я подумал над примером, демонстрирующим как входные, так и выходные генерики. Я считаю, что этот код довольно уродливый и вряд ли представляет подлинный код. Тем не менее, это должно быть достаточно хорошо для примера.
Чтобы позвонить, вы должны использовать код ниже:
Котлин
xxxxxxxxxx
1
// Least simplified
2
doStuff(1, 2L, MyJavaInterfaceWithGenerics<Int, Long, String> { a, b -> "hi" })
3
// Determine types from lambda instead of defining on the interface
4
doStuff(1, 2L, MyJavaInterfaceWithGenerics { a: Int, b: Long -> "hi" })
5
// Simplified some more
6
doStuff(1, 2L, { a, b -> "hi" })
7
// Fully simplified (extract lambda out of brackets)
8
doStuff(1, 2) { a, b -> "hi" }
9
// Determine type information from lambda (not all types had to be provided here)
10
// Removing the [Long] type will cause the compiler to choose an [Int] instead
11
doStuff(1, 2) { a, b: Long -> "hi" }
Как я упоминал секунду назад, реально, типовые входные типы A
и B
в этом примере будут предоставляться из внешнего источника. Кроме того, универсальные типы будут указаны в всеобъемлющем классе или будут привязаны к конкретным типам с самого начала.
Если вам понравился этот пост или вы нашли его полезным (или и тем, и другим), пожалуйста, не стесняйтесь подписаться на меня в Твиттере на @LankyDanDev и не забудьте поделиться с кем-либо еще, кто может найти это полезным!