Стримы java. Стримы. 1 Функциональные интерфейсы, основные типы
Скачать 149.5 Kb.
|
17) Любую ли лямбду можно заменить на анонимный класс? В Java 8 можно любой анонимный класс, который реализует один абстрактный метод заменить на лямбду выражение, если анонимный класс реализует два абстрактных метода, то заменить на лямбду будет невозможно. Если вы проект перевели на Java 8, то такие измения безопасные. Idea дает возможность поменять все анонимные классы на лямбды. 19) императивный vs декларативный подход Императивный стиль Это такой стиль программирования, при котором вы описываете, как добиться желаемого результата. Например я пишу: — поставь сковородку на огонь; — возьми два яйца (куриных); — нанеси удар ножом по каждому; — вылей содержимое на сковородку; — выкинь скорлупу; … Это что ни на есть декларативный стиль, но при этом с примесью императивного. Декларативный стиль Такой стиль, в котором вы описываете, какой именно результат вам нужен. Тут я просто пишу: — приготовь яичницу И получатель такого сообщения уже сам разбирается, какие шаги для этого надо предпринять. Почему пример из части про императивность на самом деле с примесью декларативности? Ну хотя бы потому что получатель должен знать, что такое сковорода и яйца. Императивный подход (как): Я вижу, что тот угловой столик свободен. Мы пойдём туда и сядем там. Декларативный подход (что): Столик для двоих, пожалуйста. Императивный подход означает то, как вы займёте место. Вы должны перечислить все шаги этого процесса. Декларативный же подход заявляет, что вам нужен столик на двоих 20) Что такое default методы в интерфейсе и для чего они были введены? Default-методы появились Java 8. Default-метод — это метод, который реализуется прямо в интерфейсе, его помечают ключевым словом default. default-методы упрощают рефакторинг — а именно, добавление новых методов. До Java 8 все методы в интерфейсах были абстрактными. К чему это вело? К тому, что при добавлении нового метода в интерфейс приходилось править все классы, реализующие интерфейс — реализовывать метод в этих классах. Это было неудобно. А в Java 8 (в классы ядра) захотели ввести новые методы в старые интерфейсы. Так что ввели ключевое слово default и эти методы сделали default. Например, в интерфейсе java.lang.Iterable появились новые default-методы forEach() и spliterator(): какой метод унаследует класс, реализующий два интерфейса, если оба из них содержат default-методы с одинаковыми именами. Чтобы не было неопределенности (и чтобы скомпилировался код), мы обязаны переопределить в Kentavr метод sleep() , причем можно просто вызвать в нем метод sleep() любого из интерфейсов — Man либо Animal, указав через точку и super, чей именно метод нужен: 21) К каким переменным есть доступ из лямбда-выражения? Доступ к переменным внешней области действия из лямбда-выражения очень схож к доступу из анонимных объектов. Можно ссылаться на: 1) неизменяемые (effectively final - не обязательно помеченные как final) локальные переменные; 2) поля класса; 3) статические переменные. К методам по умолчанию реализуемого функционального интерфейса обращаться внутри лямбда-выражения запрещено. effectively final локальные переменные В в лямбда-выражениях стоит использовать внешние (относительно выражения) неизменяемые значения, а не внешние переменные, значение и внутреннее состояние которых могут меняться. Под внешними неизменяемыми значениями, соответственно, подразумеваются effectively final локальные переменные и поля примитивных типов, а также effectively final объекты, внутреннее состояние которых не будет меняться. Связано это с тем, что Streams и лямбда-выражения проектировались из расчета на их многопоточное использование. 22) Любой анонимный класс можно заменить на лямбду? В Java 8 можно любой анонимный класс, который реализует один абстрактный метод заменить на лямбду выражение, если анонимный класс реализует два абстрактных метода, то заменить на лямбду будет невозможно. 23) 24) Отличие BinaryOperator от Function. BinaryOperator возвращает тип данных тот же над которым производились действия. Функциональный интерфейс Function 25) Что такое ленивая инициализация стрима? Отложенная (ленивая) инициализация (англ. ... Lazy initialization) — приём в программировании, когда некоторая ресурсоёмкая операция (создание объекта, вычисление значения) выполняется непосредственно перед тем, как будет использован её результат. Ленивая инициализация-это оптимизация производительности, при которой вы откладываете (потенциально дорогостоящее) создание объекта до тех пор, пока оно вам действительно не понадобится. Секрет «ленивости» Stream в том, что каждый раз, когда вы используете Stream, он соединяет несколько промежуточных операций и присоединяет конечную операцию в конце. Такие методы, как map () и filter (), являются промежуточными операциями, и при их вызове немедленно возвращается другой объект Stream. Для таких методов, как reduced () и findFirst (), они являются конечными операциями, а реальные операции выполняются при их вызове для получения требуемых значений. 26) Две терминальные операции в одном выражении? Терминальная операция это, то что запускает стрим и в тоже самое время она получает результат, а значит она должна быть одна 27) Что такое терминальная операция? Это операции, которые как бы «запускают» наш стрим. Мы можем создать стрим и добавить в него любое количество промежуточных операций, но они не будут выполнены пока не будут добавлена терминальная операция. Выше мы уже применяли одну из самых популярных операций — forEach(Consumer В нее попадают все прошедшие через стрим объекты и обрабатываются в соответствие с тем алгоритмом, что будет указан в Consumer. 28) Что возвращают промежуточные операции над стримом? Промежуточные (“intermediate”, ещё называют “lazy”) операции — обрабатывают поступающие элементы и возвращают стрим. Промежуточных операторов в цепочке обработки элементов может быть много Промежуточные операции следует воспринимать как «отложенные», т.е. они не меняют сами данные, а только задают правила их изменения. А терминальные как раз инициируют всю цепочку преобразований и возвращают модифицированные данные. 29) Для чего нужны параллельные стримы? Stream API предоставляет очень простой механизм для выполнения операций над потоком параллельно: входной поток разбивается на части, если это возможно, и каждая такая часть обрабатывается параллельно с остальными, в раздельных нитях. Кроме последовательных потоков Stream API поддерживает параллельные потоки. Распараллеливание потоков позволяет задействовать несколько ядер процессора (если целевая машина многоядерная) и тем самым может повысить производительность и ускорить вычисления. В то же время говорить, что применение параллельных потоков на многоядерных машинах однозначно повысит производительность - не совсем корректно. В каждом конкретном случае надо проверять и тестировать. есть 3 минуса 1) если у вас есть какое-то количество потоков, обрабатывающих запросы пользователей и в каждом потоке выполняются какие-либо операции над parallelStream(), эти потоки будут вынуждены ждать друг друга 2) уследить за нежелательными эффектами от влияния нитей друг на друга становится ещё сложнее. 3) параллельность обработки вообще не гарантируется и зависит от источника данных. А прирост производительности зависит от его способности корректно разделить набор данных на независимые блоки. 30) Что такое анонимный класс. Анонимный класс (anonymous class) - это локальный класс без имени. Используется тогда, когда нужно переопределить метод класса или интерфейса. 31) Что такое функциональный интерфейс и для чего он нужен и зачем были добавлены? Если интерфейс в Java содержит один и только один абстрактный метод, то он называется функциональным. Этот единственный метод определяет назначение интерфейса. Это интерфейс, котрый определяет сторого один метод. аннотанация @FunctionalInterface введена для обазначения интерфейса, функциональным, это анотанация используется для того, чтобы избежать случайного добавления абстрактных методов в функциональном интерфейсе. Она не обязательна, но является хорошей практикой чистого кода ФИ позволяют нам использовать лямбда выражения для создания экземпляра таких интерфейсоф Интерфейс Runnable является одним из самых популярных, с одним методом run(). интерфейс может содержать сколько угодно default методов и при этом оставатьсяя функциональным, потому, что default методы не абстрактные 32) Какой аннотацией помечается функциональный интерфейс? аннотанация @FunctionalInterface 33) Сколько дефолтных методов и статических методов, сртатических полей в интерфейсе? Сколько хочешь, статик методы переопределять нельзя. Статик поля возможны, но это плохая практика. 34) Где находятся функциональные интерфейсы? В Java все стандартные функциональные интерфейсы лежат в пакете java.util.function 35) Перечислить основные семейства функ.интерфейсов? Делятся на 7 семейств 1) Consumer - потребители, те кто принимают(объект типа Т, совершают некотрые действия) но не возвращают в замен. Его подвиды IntConsumer, LongConsumer, DoubleConsumer они есть потому, что дженерики не могут парметризоваться примитивами. 2) Supplir - поставщики, они не принимают ни какое значение, а просто возращают какоето значение типа Т. 3) Predicate - приниает выражение какого то типа (для проверки некоторого условия) а наружу выдает булевское значение. есть для примитивов, есть булевские 4) Function - принимает аргумент типа Т и приводит его к объекту типа R, который и возвращается как результат, в общем случаее типы разные. Бывает принимают два параметра. могут стоять примитивы 5) Operator - это частный случай функции, когда на вход подается значение одного и того же типа. Унарный оператор принимает один параметр а Бинарный два параметра отдельные интерфейсы над лонгами и даблами 6) BinaryOperator 7) UnaryOperator 36) Функциональные интерфейсы что они принимают и что возвращают? 1) Consumer - потребители, те кто принимают (объект типа Т, совершают некотрые действия) но не возвращают в замен. Его подвиды IntConsumer, LongConsumer, DoubleConsumer они есть потому, что дженерики не могут парметризоваться примитивами. 2) Supplir - поставщики, они не принимают ни какое значение, а просто возращают как оето значение типа Т. 3) Predicate - приниает выражение (для проверки некоторого условия) какого то типа а наружу выдает булевское значение. есть для примитивов, есть булевские 4) Function - принимает аргумент типа Т и приводит его к объекту типа R, который и возвращается как результат, в общем случаее типы разные. 5) Operator - это частный случай функции, когда на вход подается значение одного и того же типа. Унарный оператор принимает один параметр а Бинарный два параметра отдельные интерфейсы над лонгами и даблами 6) BinaryOperator 7) UnaryOperator 37) Какие есть способы инстацировать функциональные интерфейсы? Функциональные интерфейсы можно интанцировать тремя способами 1) можно завести именнованный или анонимный класс, но это громоздко 2) можно использовать лямбда выражение. Обявляем имена параметра и тело метода sqware = x-> {return x * x}; обязательно точка с запятой. Если тело метода из одного метода, то скобки можно опустить а если есть ретерн то добавляем скобки вопрос к каким переменным и как можно обращатся в лямбда выражении 1) к параметрам лямбды, а также свободно объявлять и использовать любые переменные х -> x * x; 2) к полям(переменным) того класса в нутри котого объявлена лямбда, можно как читать, так и писать. IntSupplier sequense = () -> counter++; 3) к переменным которые объявлены внутри метода где объявлена лямбда. Но есть ограничения переменные должны быть эффективно финальные т.е. значение им должно присвоено ровно один раз до объявления лямбды, после чего оно менятся уже не может.(Типа мы написали final)int bonus = 10; IntUnaryOperator bonusAdder = (x) -> x + bonus; !!! Лямбдам нельзя присваивать новые значения переменным содержащимся в ее методе. Для обхода этого ограниечения используют трюк с обходом массива единичной длины. 3) с помощью ссылки на метод ToIntFunction Consumer |