У меня возникли вопросы о лямбда-выражениях и RxJava. Эти вопросы в основном касаются не полного понимания лямбда-выражений или RxJava. Я попытаюсь объяснить лямбда-выражения как можно проще. RxJava я опишу отдельно.
Лямбда-выражения и RxJava
Что такое лямбда-выражения? Лямбда-выражения – это «всего лишь» новый способ сделать то же самое, что мы всегда могли сделать, но в более чистом и менее многословном новом способе использования анонимных внутренних классов.
Анонимный внутренний класс в Java – это класс без имени, он должен использоваться, если вам необходимо переопределить методы класса или интерфейса. Анонимный внутренний класс может быть создан из класса или интерфейса.
Например:
abstract class Animal {
abstract void speak();
}
Animal a = new Animal() {
void speak() {
System.out.println("Woff");
}
};
В Android мы обычно используем анонимный внутренний класс в качестве слушателя, например, для кнопок такого рода:
Button btn = findViewById(R.id.button);
btn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(final View view) {
// Do some fancy stuff with the view parameter.
}
}
);
Вернемся к лямбда-выражениям. Это следующая часть, которая является частью предыдущего кода, который считается анонимным внутренним классом.
new View.OnClickListener() {
@Override
public void onClick(final View view) {
// Do some fancy stuff with the view parameter.
}
}
Лямбда-выражения могут использоваться только в том случае, если вам нужно переопределить не более одного метода. К счастью для нас, View.OnClickListener содержит только один. Посмотрите на код ниже. Как думаете, какую его часть нам придётся убрать?
new View.OnClickListener() {
@Override
public void onClick(final View view) {
// Do some fancy stuff with the view parameter.
}
}
После удаления практически всего кода, нам нужно добавить ->, как в приведенном ниже коде. Входящий параметр view может использоваться внутри функции так же, как и раньше.
(view) -> {
// Do some fancy stuff with the view parameter.
}
В некоторых случаях вам может потребоваться добавить тип к параметру, если компилятору не удается его угадать, вы можете сделать это, добавив его перед параметром:
(View view) -> {
// Do some fancy stuff with the view parameter.
}
Вы также можете использовать многострочный код:
(view) -> {
Intent intent = new Intent(context, AuthActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
view.getContext().startActivity(intent);
}
Если у вас есть интерфейс с методом, принимающим два параметра…
interface MyInterface {
abstract void onMethod(int a, int b);
}
…лямбда-выражение будет выглядеть следующим образом:
(a, b) -> {
// Do some fancy stuff with the a and b parameter.
}
Если метод имеет возвращаемый тип…
interface MySecondInterface {
abstract int onSecondMethod(int a, int b);
}
…лямбда-выражение будет выглядеть следующим образом:
(a, b) -> {
return a + b;
}
Но это еще не все, есть некоторые специальные случаи, которые делают код еще меньше. Если тело вашего метода содержит только одну строку кода, вы можете удалить фигурные скобки {}. Если вы удаляете фигурные скобки, вам также необходимо удалить точку с запятой, оставив следующее:
(a, b) -> return a + b
Есть еще одна вещь, которую мы можем сделать. Если у нас только одна строка кода, то компилятор может понять, нужна ли возвращаемая часть или нет, поэтому мы можем оставить ее следующим образом:
(a, b) -> a + b
Если бы у нас был многострочный код, это свелось бы к следующему:
(a, b) -> {
a+=1;
return a + b;
}
Так что же мы сделали? Мы взяли это:
new MySecondInterface() {
@Override
public int onSecondMethod(final int a, final int b) {
return a + b;
}
};
и превратили вот в это:
(a, b) -> a + b
Осталось только одна вещь, ссылки на методы. Допустим, у нас есть интерфейс, как и раньше, и метод, который этот интерфейс принимает как параметр:
public interface Callback {
public void onEvent(int event);
}
public void myMethod(Callback callback){
}
Без лямбда-выражения это выглядело бы так:
myMethod(new Callback() {
@Override
public void onEvent(final int state) {
System.out.println(state);
}
});
Добавив лямбда-выражение, как мы это делали раньше, получим следующее:
myMethod(state -> System.out.println(state));
Но это еще не все. Если используемый код – однострочный и вызываемая функция принимает один параметр, мы можем передавать ссылку на метод в таком виде:
myMethod(System.out::println);
Параметр будет передаваться автоматически, без необходимости использования другого кода! Удивительно, правда? Надеюсь, вы узнали что-то новое!
Комментарии (6)
Barrya42
08.08.2018 10:25-1Для начинающих возможно и есть что то новое, а для тех кто с ними работал все это и так знает.
alfaneos
08.08.2018 10:25+1У меня есть вопрос по поводу читаемости кода. Вот например строка сокращенная лямбда-выражением
(a, b) -> a + b
В данном случае ясно что правая часть возвращается, но если там будет стоять вызов функции который явно или неявно возвращает данные. Стоит ли тогда убирать return? Я новичок, поэтому не знаю как будет правильноDevcolibri Автор
08.08.2018 10:28-2В общем-то и в случае с функцией будет вполне понятно, что её результат будет возвращаться
TechnoMag
08.08.2018 20:02Спасибо. Это основы, но изложено все доходчиво. Хотелось бы видеть продолжение статьи относительно RxJava + Kotlin: часть смотришь на код Kotlin и сложно понять, как это выглядит в Java.
Sultansoy
09.08.2018 18:40По-моему слишком базовые вещи рассмотрены. Еще непонятно, зачем в интерфейсах слово abstract писать? Такая статья больше подойдет для каких-то онлайн курсов.
Handen
Спасибо