Статьи

Совет: пишите более чистый код с помощью Kotlin SAM Conversions

Если вы опытный разработчик приложений для Android, вы, вероятно, привыкли к многословию Java 7. В результате вы можете найти краткий синтаксис Kotlin, который ориентирован на функциональных программистов, немного тревожный.

Одной из распространенных проблем, с которыми сталкиваются новички при изучении Kotlin, является понимание того, как он ожидает, что вы будете работать с интерфейсами Java, которые содержат один метод. Такие интерфейсы повсеместны в мире Android и часто называются интерфейсами SAM, где SAM — сокращение от Single Abstract Method.

В этом коротком руководстве вы узнаете все, что нужно для правильного использования SAM-интерфейсов Java в коде Kotlin.

Если вы хотите использовать интерфейс Java, содержащий один метод в коде Kotlin, вам не нужно вручную создавать анонимный класс, который его реализует. Вместо этого вы можете использовать лямбда-выражение. Благодаря процессу, называемому преобразованием SAM, Kotlin может прозрачно преобразовать любое лямбда-выражение, сигнатура которого совпадает с сигнатурой единственного метода интерфейса, в экземпляр анонимного класса, который реализует интерфейс.

Например, рассмотрим следующий Java-интерфейс с одним методом:

1
2
3
public interface Adder {
    public void add(int a, int b);
}

Наивный и Java 7-подобный подход к использованию вышеуказанного интерфейса будет включать в себя работу с выражением object и будет выглядеть следующим образом:

1
2
3
4
5
6
7
// Creating instance of an anonymous class
// using the object keyword
val adder = object : Adder {
    override fun add(a: Int, b: Int): Int {
        return a + b
    }
}

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

1
2
// Creating instance using a lambda
val adder = Adder { a, b -> a + b }

Как вы можете видеть, теперь мы заменили анонимный класс коротким лямбда-выражением, которому предшествует имя интерфейса. Обратите внимание, что число аргументов, которые принимает лямбда-выражение, равно количеству параметров в сигнатуре метода интерфейса.

Работая с классами Java, имеющими методы, которые принимают типы SAM в качестве аргументов, вы можете еще больше упростить приведенный выше синтаксис. Например, рассмотрим следующий класс Java, который содержит метод, который ожидает объект, реализующий интерфейс Adder :

01
02
03
04
05
06
07
08
09
10
11
public class Calculator {
    private Adder adder;
 
    public void setAdder(Adder adder) {
        this.adder = adder;
    }
 
    public void add(int a, int b) {
        Log.d(«CALCULATOR», «Sum is » + adder.add(a,b));
    }
}

В вашем коде Kotlin теперь вы можете напрямую передавать лямбда-выражение в метод setAdder() без setAdder() префикса имени интерфейса Adder .

1
2
val calculator = Calculator()
calculator.setAdder({ a, b -> a+b })

Стоит отметить, что при вызове метода, который принимает тип SAM в качестве единственного аргумента, вы можете пропустить скобки, чтобы сделать ваш код еще более сжатым.

1
calculator.setAdder { a, b -> a+b }

Если вы думаете, что лямбда-выражения сбивают с толку, у меня есть для вас хорошая новость: преобразования SAM также отлично работают с обычными функциями. Например, рассмотрим следующую функцию, подпись которой совпадает с сигнатурой метода интерфейса Adder :

1
2
3
4
5
6
7
fun myCustomAdd(a:Int , b:Int):Int =
   if (a+b < 100)
       -1
   else if (a+b < 200)
       0
   else
       a+b

Kotlin позволяет вам напрямую передавать myCustomAdd() в качестве аргумента setAdder() класса Calculator . Не забудьте сослаться на метод с помощью оператора :: . Вот как:

1
calculator.setAdder (this::myCustomAdd)

Часто интерфейсы SAM содержат однопараметрические методы. Метод с одним параметром, как следует из его названия, имеет только один параметр в своей сигнатуре. Работая с такими интерфейсами, Kotlin позволяет вам пропустить параметр в сигнатуре вашего лямбда-выражения и использовать неявную переменную, называемую it в теле выражения. Чтобы прояснить ситуацию, рассмотрим следующий интерфейс Java:

1
2
3
public interface Doubler {
    public int doubleIt(int number);
}

При использовании интерфейса Doubler в вашем коде Kotlin вам не нужно явно указывать параметр number в сигнатуре вашего лямбда-выражения. Вместо этого вы можете просто сослаться на это как на it .

1
2
3
4
5
// This lambda expression using the it variable
val doubler1 = Doubler { 2*it }
 
// is equivalent to this ordinary lambda expression
val doubler2 = Doubler { number -> 2*number }

Как разработчик Java, вы можете быть склонны создавать SAM-интерфейсы в Kotlin. Однако делать это обычно не очень хорошая идея. Если вы создаете интерфейс SAM в Kotlin или создаете метод Kotlin, который ожидает, что объект, реализующий интерфейс SAM в качестве аргумента, средство преобразования SAM будет вам недоступно — преобразование SAM является функцией взаимодействия Java и ограничено Java только классы и интерфейсы.

Поскольку Kotlin поддерживает функции более высокого порядка — функции, которые могут принимать другие функции в качестве аргументов — вам никогда не понадобится создавать в нем интерфейсы SAM. Например, если класс Calculator переписан в Kotlin, его setAdder() может быть написан так, что он напрямую принимает функцию в качестве аргумента вместо объекта, реализующего интерфейс Adder .

1
2
3
4
5
6
7
8
9
class Calculator {
    var adder:(a:Int, b:Int)->Int = {a,b -> 0}
                                    // Default implementation
                                    // Setter is available by default
 
    fun add(a:Int, b:Int) {
        Log.d(«CALCULATOR», «Sum is » + adder(a,b))
    }
}

Используя вышеупомянутый класс, вы можете установить adder для функции или лямбда-выражения, используя оператор = . Следующий код показывает вам, как:

1
2
3
4
val calculator = Calculator()
calculator.adder = this::myCustomAdd
// OR
calculator.adder = {a,b -> a+b}

API-интерфейсы Android в основном написаны на Java, и многие широко используют интерфейсы SAM. То же самое можно сказать и о большинстве сторонних библиотек. Используя методы, которые вы изучили в этом руководстве, вы можете работать с ними в своем коде Kotlin в краткой и удобной для чтения форме.

Чтобы узнать больше о возможностях взаимодействия Java с Kotlin, обратитесь к официальной документации . И ознакомьтесь с некоторыми другими нашими учебниками по разработке приложений для Android!

  • Android SDK
    Android O: проверка номера телефона с помощью SMS-токенов
    Чике Мгбемена
  • Android вещи
    Вещи Android: создание облачного сервисного швейцара
    Пол Требилкокс-Руис
  • Android SDK
    Создайте интеллектуальное приложение с Google Cloud Speech и API на естественном языке