Discovering Lambdas in Java 8

Preview:

Citation preview

Discovering Lambdas in Java8

by Aleksandra Dmytrenko

План

Старая добрая Java -> Функциональное программирование

Как посчитать факториал?

Слон и моська в одном

Функциональные интерфейсы

Функциональное

программирование!

Чем ФП приятно?

Функция не может поменять значение переменной вне своей видимости, все

переменные final (рай для юнит тестов)

Внешние состояние не влияет на функцию (удобно отлаживать и замещать части

кода).

Встроенная многопоточность (кому нравится думать про «разделяй и

властвуй?»)

Ленивые (отложенные) вычисления - выполняется только нужный код в момент,

когда надо результат.

Чем ФП чревато?

Состояние не хранится (нет переменных)

Все переменные final или const, нет public (как вам код с такими

ограничениями?)

Встроенная многопоточность (к сожалению пока не на высшем уровне)

Ленивые (отложенные) вычисления - выполняется только используемый код в

«когда захочу» момент. Тяжело контролировать очередность действий.

К делу

Как определить, простое ли число?

Легким движением руки

императивное

программиро-вание

превращается в

декларативное

КАК делаем -> ЧТО делаем?

Imperative style:

public boolean isPrime(int number) {

for (int curNumb = 2; curNumb <= number/2; curNumb++) {

if (number % curNumb == 0) {

return false;

}

}

return true;

}

DECLARATIVE STYLE:public boolean isPrimeDeclarative(final int number) { return IntStream.rangeClosed(2, number/2) .noneMatch(curNumb -> number % curNumb == 0);}

Добавим проверку на знак числа

public boolean isPrimeImperative(int number) {

if (number < 0) {

return false;

}

for (int curNumb = 2; curNumb <= number / 2; curNumb++) {

if (number % curNumb == 0) {

return false;

}

}

return true;

}

public boolean isPrimeDeclarativeNegativeNumbCheck(final int number) { return number >= 0 && IntStream.rangeClosed(2, number.2).noneMatch(curNumb -> number % curNumb == 0);}

Ну… надо бы еще на что-то

посмотреть

Кого на собеседовании не просили посчитать

факториал?

public static int factorialOf(int number) {

if (number >= 0) {

int factorial = 1;

for (int curNumb = 2; curNumb <= number; curNumb++){

factorial = factorial * curNumber;

}

return factorial;

} else throw new IllegalArgumentException("Factorial can be counted only of not negative numbers.");

}

6 строчек!

public static int factorialOf(int number) {

if (number > 0) return number*factorialOf(number-1);

if (number == 0) return 1;

else throw new IllegalArgumentException("Factorial can be counted only of not negative numbers.");

}

3 строчки!

Факториал через цикл каждый напишет. А

рекурсия во что обойдется?

public static int factorialOf(int number) { if (number >= 0) return IntStream.rangeClosed(2, number) .reduce(1, (accResult, curNumb) -> accResult * curNumb); else throw new IllegalArgumentException("Factorial can be counted only of not negative numbers.");}

3 строчки!

– Я и многие другие

“Не делайте что-то только потому, что вы можете это сделать.

Делайте красиво.”

Chuck Norris can do multiple inheritance in

Java

Я скажу вам больше, не

только он может. Вы тоже! С

помощью интерфейсов,

правда.

А что если бы Cлон был Моськой, а Моська Cлоном?

interface Elephant {

default String makeSound(String name) {

return name + ": Не злите меня!";

}

}

interface Dog {

default String makeSound(String name) {

return name + ": Гав-гав";

}

}

class EveryDogWantsToBeAnElephant implements Dog, Elephant {

@Override

public String makeSound(String name) {

return name + ": Я спокоен";

}

public static void main(final String[] args) {

EveryDogWantsToBeAnElephant elephantDog = new EveryDogWantsToBeAnElephant();

Elephant e = new Elephant(){};

Dog d = new Dog(){};

System.out.println(e.makeSound("Слон"));

System.out.println(d.makeSound("Моська"));

System.out.println(elephantDog.makeSound("Моська-слон"));

}}

А что если бы Cлон был Моськой, а Моська Cлоном?

Слон: Не злите меня!Моська: Гав-гавМоська-слон: Я спокоен

Множественное наследование - это когда есть дефолтная

реализация метода с одинаковым названием в каждом

интерфейсе.

А как себя будут вести статические и абстрактные методы?

Статические методы

К ним мы обращаемся как

НазваниеКласса.имяМетода. По сути эта

комбинация всегда уникальна и ее можно

воспринимать целиком, как название

interface Dog {

static String walk() { return "Я бегаю быстренько своими маленькими лапками.»; }}

interface Elephant {

static String walk() { return "Я большой и ступаю тихо но тяжело."; }}—————————————————————————————————————System.out.println(Elephant.walk());System.out.println(Dog.walk());

OUTPUT:

Я большой и ступаю тихо но тяжело.Я бегаю быстренько своими маленькими лапками.

Абстрактные методы

Когда мы инстанциируем абстрактный класс (интерфейс), то надо реализовать

все абстрактные методы.

Но если мы имплементируем интерфейсы с одинаковыми названиями методов,

достаточно описать только один из них.

Пример: Comparator с его int compare(T o1, T o2),

Comparable с его int compareTo(T o)

и другие

Интерфейс, у которого есть только один абстрактный метод

называется функциональным интерфейсом

1. Можно по-старинке:

Elephant e = new Elephant() {

@Override

public boolean isProud() {

return false;

}

};

2. Можно по-модному:

Elephant e = () -> { return false; };

3. А можно без лишних слов:

Elephant e = () -> false;

Реализуем функциональный интерфейс

interface Elephant {

boolean isProud();

}

interface Dog {

boolean isProud();

}

System.out.println("Слон горделив: " + e.isProud());

System.out.println("Моська горделива: " + d.isProud());

System.out.println("Моська-слон горделив: " + elephantDog.isProud());

OUTPUT:

Слон горделив: false

Моська горделива: true

Моська-слон горделив: false

Функциональный интерфейс вызываем точно

так же

Подробнее про

Предикаты

1. Обычный предикат:public Predicate<Integer> isGreaterThan2New() { return a -> a > 2;}Как это выглядело раньше:public boolean isGreaterThan2Old(Integer a) { return a > 2;}

2. Обычный би-предикат:public BiPredicate<Integer, Integer> isGreaterThanFunc() { return (a, b) -> a > b;}

Как это выглядело раньше:public boolean isGreaterThan(Integer a, Integer b) { return a > b;}

Всегда возвращают

булеан, реализуют

интерфейс с методом

boolean test(T t);

Если реализуется метод boolean test(T t), почему нигде нет этого

названия?

Подробнее про

Функции

1. Обычный предикат:public Function<Integer, Integer> multiplyFuncBy2() { return (a) -> a * 2;}Как это выглядело раньше:public Integer multiplyBy2(Integer a) { return a * 2;}

2. Обычный би-предикат:public BiFunction<Integer, Integer, Integer> multiplyFuncBy() { return (a, b) -> a * b;}Как это выглядело раньше:public Integer multiplyFuncBy(Integer a, Integer b) { return a * b;}

В отличие от предикатов,

возвращают тип, указанный

на последнем месте и

принимает типы, указанные

вначале

Как применяем предикаты

1. Обычный и би-предикат, аналогичные методы:boolean actual = p.isGreaterThan2New().test(4);boolean actual = p.isGreaterThan2Old(4);p.isGreaterThanNew().test(4, 8);p.isGreaterThanOld(4, 8);

2. Предикат и соответствующий метод в стриме:values.stream().filter(p.isGreaterThan2New( )).collect(Collectors.toList());List<Integer> actual = values.stream().filter(v -> p.isGreaterThan2(v)).collect(Collectors.toList());

Нету параметра. Стрим сам

передает.

Как применяем функции?

1. Обычная и би-функция, аналогичные методы:int actual = f.multiplyFuncBy2().apply(5);int actual = f.multiplyBy2(5);f.multiplyFuncBy().apply(5, 3);f.multiplyBy(5, 3);

2. Функция и соответствующий метод в стриме: values.stream().map(f.multiplyFuncBy2()).skip(2).limit(2).collect(Collectors.toList());List<Integer> actual = values.stream().map(v -> f.multiplyFuncBy().apply(v, 3)).skip(2).limit(2).collect(Collectors.toList());

Параметр передается через apply()

Подробнее про

Consumer - потребитель

1. Обычный void метод превращается в консьюмер:public <T> void useConsumer(T t){ System.out.print("Consumer says: " + t);}public <T> Consumer<T> useConsumerFunc(){ return (t) -> System.out.print("Consumer says: " + t);}

2. Разница в использовании:consumer.useConsumer("Hello ;)"); consumer.useConsumerFunc().accept("Hello ;)");

3. Использование консьюмера в стриме: Stream.of(1, 6).forEach(consumer.useConsumerFunc() .andThen(l -> System.out.print("\n")));OUTPUT:

Consumer says: 1Consumer says: 6

Подробнее про

Supplier - поставщик

1. 2 метода - обычный и поставщик    public String useSupplier() { return Thread.currentThread().getStackTrace()[1].getMethodName(); }

public Supplier<String> useSupplierFunc() { System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); return () -> Thread.currentThread().getStackTrace()[1].getMethodName(); }

Supplier - поставщик

1. Тесты:String actual = supplier.useSupplier();String actual = supplier.useSupplierFunc().get();Stream.of("1 ").forEach(n -> System.out.println(n + supplier.useSupplierFunc()));

2. Вывод:1) useSupplier2) useSupplierFunc lambda$useSupplierFunc$03) useSupplierFunc 1 lambdas.functionalInterface.MySupplier$$Lambda$1/1449621165@3d8c7aca

Это совсем не все, но

основное, что надо знать о

Java

Тут код с презентации и слайды

https://github.com/olexandra-dmytrenko/LambdasForConfs.git

Спасибо за внимание :)

Recommended