Главная страница
Навигация по странице:

  • Почему пример из части про императивность на самом деле с примесью декларативности

  • 20) Что такое default методы в интерфейсе и для чего они были введены

  • До Java 8 все методы в интерфейсах были абстрактными. К чему это вело

  • 21) К каким переменным есть доступ из лямбда-выражения

  • 22) Любой анонимный класс можно заменить на лямбду

  • 25) Что такое ленивая инициализация стрима

  • 26) Две терминальные операции в одном выражении

  • 27) Что такое терминальная операция

  • 28) Что возвращают промежуточные операции над стримом

  • 29) Для чего нужны параллельные стримы

  • 31) Что такое функциональный интерфейс и для чего он нужен и зачем были добавлены

  • 32) Какой аннотацией помечается функциональный интерфейс

  • 34) Где находятся функциональные интерфейсы

  • 36) Функциональные интерфейсы что они принимают и что возвращают

  • 37) Какие есть способы инстацировать функциональные интерфейсы

  • 39) К каким переменным и как можно обращаться в теле лямда-выражениях

  • Стримы java. Стримы. 1 Функциональные интерфейсы, основные типы


    Скачать 149.5 Kb.
    Название1 Функциональные интерфейсы, основные типы
    АнкорСтримы java
    Дата26.01.2022
    Размер149.5 Kb.
    Формат файлаdoc
    Имя файлаСтримы.doc
    ТипДокументы
    #342749
    страница2 из 3
    1   2   3

    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 представляет функцию перехода от объекта типа T к объекту типа R.


    25) Что такое ленивая инициализация стрима?

    Отложенная (ленивая) инициализация (англ. ... Lazy initialization) — приём в программировании,

    когда некоторая ресурсоёмкая операция (создание объекта, вычисление значения)

    выполняется непосредственно перед тем, как будет использован её результат. Ленивая инициализация-это оптимизация производительности,

    при которой вы откладываете (потенциально дорогостоящее) создание объекта до тех пор, пока оно вам действительно не понадобится.

    Секрет «ленивости» Stream в том, что каждый раз, когда вы используете Stream,

    он соединяет несколько промежуточных операций и присоединяет конечную операцию в конце.

    Такие методы, как map () и filter (), являются промежуточными операциями, и при их вызове немедленно возвращается другой объект Stream.

    Для таких методов, как reduced () и findFirst (),

    они являются конечными операциями, а реальные операции выполняются при их вызове для получения требуемых значений.

    26) Две терминальные операции в одном выражении?

    Терминальная операция это, то что запускает стрим и в тоже самое время она получает результат, а значит она должна быть одна


    27) Что такое терминальная операция?

    Это операции, которые как бы «запускают» наш стрим. Мы можем создать стрим и добавить в него любое количество промежуточных операций,

    но они не будут выполнены пока не будут добавлена терминальная операция.
    Выше мы уже применяли одну из самых популярных операций — forEach(Consumer action).

    В нее попадают все прошедшие через стрим объекты и обрабатываются в соответствие с тем алгоритмом, что будет указан в 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 принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию и возвращает ее результат также в виде объекта типа T.

    7) UnaryOperator принимает в качестве параметра объект типа T, выполняет над ними операции и возвращает результат операций в виде объекта типа T.

    36) Функциональные интерфейсы что они принимают и что возвращают?

    1) Consumer - потребители, те кто принимают (объект типа Т, совершают некотрые действия) но не возвращают в замен. Его подвиды IntConsumer, LongConsumer, DoubleConsumer они есть

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

    2) Supplir - поставщики, они не принимают ни какое значение, а просто возращают как оето значение типа Т.

    3) Predicate - приниает выражение (для проверки некоторого условия) какого то типа а наружу выдает булевское значение. есть для примитивов, есть булевские

    4) Function - принимает аргумент типа Т и приводит его к объекту типа R, который и возвращается как результат, в общем случаее типы разные.

    5) Operator - это частный случай функции, когда на вход подается значение одного и того же типа. Унарный оператор принимает один параметр а Бинарный два параметра

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

    6) BinaryOperator принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию и возвращает ее результат также в виде объекта типа T.

    7) UnaryOperator принимает в качестве параметра объект типа T, выполняет над ними операции и возвращает результат операций в виде объекта типа T.


    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 intParser = Integer :: parseInt;

    Consumer printer= System.out:: println;

    Function objectStringFunction = Object:: toString;

    IntFunction arrayAllocator = String[] :: new;

    Есть нюансы!!!!! М ыможем ссылаться на статический метод указывая имя класса двоеточие и имя статического метода.

    Если на нестатический метод, указывая конкретно объект и два двоеточия

    Есть вариант указать имя класса два двоиточия и имя нестатического метода, но тогда первый указываемый параметре будет тем объектом на который не стаитический метод будет вызван.

    Можно ссылаться на конструктор. Когда пишут имя класса два двоеточия и ключевое слово new


    38) Что такое лямбда выражение и ссылка на метод. Как они связаны с анонимным классом.

    Лямбда представляет набор инструкций, которые можно выделить в отдельную переменную

    и затем многократно вызвать в различных местах программы.

    Основу лямбда-выражения составляет лямбда-оператор, который представляет стрелку ->.

    Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения,

    а правая собственно представляет тело лямбда-выражения, где выполняются все действия.

    Лямбда-выражение не выполняется само по себе, а образует реализацию метода, определенного в функциональном интерфейсе.

    По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java.

    Если существующий в классе метод уже делает все, что необходимо,

    то можно воспользоваться механизмом method reference (ссылка на метод) для непосредственной передачи этого метода. на

    1) статический метод; имя_класса::имя_статического_метода для статического метода;

    2) на метод экземпляра объекта; объект_класса::имя_метода для метода экземпляра;

    3) на конструкторе. название_класса::new для конструктора.

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

    они предоставляют компилятору более качественную информацию о типе и при возможности выбора

    между использованием ссылки на существующий метод и использованием лямбда-выражения,

    следует всегда предпочитать использование ссылки на метод.



    39) К каким переменным и как можно обращаться в теле лямда-выражениях?

    Доступ к переменным внешней области действия из лямбда-выражения очень схож к доступу из анонимных объектов. Можно ссылаться на:

    1) неизменяемые (effectively final - не обязательно помеченные как final) локальные переменные;

    2) поля класса;

    3) статические переменные.

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

    effectively final локальные переменные

    В в лямбда-выражениях стоит использовать внешние (относительно выражения) неизменяемые значения, а не внешние переменные,

    значение и внутреннее состояние которых могут меняться. Под внешними неизменяемыми значениями, соответственно,

    подразумеваются effectively final локальные переменные и поля примитивных типов, а также effectively final объекты,

    внутреннее состояние которых не будет меняться.
    Связано это с тем, что Streams и лямбда-выражения проектировались из расчета на их многопоточное использование.

    //1 вариант параметрам лямбды, а также свободно объявлять и использовать любые переменные

    IntUnaryOperator squaer = x -> x * x;
    //2 вариант к полям(переменным) того класса в нутри котого объявлена лямбда, можно как читать, так и писать.

    IntSupplier sequense = () -> counter++;
    //3 вариант к переменным которые объявлены внутри метода где объявлена лямбда. Но есть ограничения переменные должны быть эффективно финальные

    // т.е. значение им должно присвоено ровно один раз до объявления лямбды, после чего оно менятся уже не может.(Типа мы написали final)

    int bonus = 10;

    IntUnaryOperator bonusAdder = (x) -> x + bonus;
    //трюк с обходом еденичной длинны

    int[] test = new int[] {0};//ссылка является эффективно финальной но на содержимое это не влияет.
    40) Что такое Stream? Какие бывают стримы (По разным критериям, например "конечные и бесконечные")

    Стрим это последовательность элементов, потенциально бесконечная, свозможность применять к ней, сложные, поэтапные приобразования

    цикла и условного оператора. И программа будет иметь, простую линейную структуру.(Владыкин)

    Стрим состаит из источника - (элементы) -> промежуточные операторв - (элементы) -> терминальный оператор

    !!! Важно обработка происходит от терминального источника

    Операторы можно разделить на две группы:

    Промежуточные или КОНВЕЕРНЫЕ операторы(“intermediate”, ещё называют “lazy”) — обрабатывают поступающие элементы и возвращают стрим.

    Промежуточных операторов в цепочке обработки элементов может быть много.

    Терминальные (“terminal”, ещё называют “eager”) — обрабатывают элементы и завершают работу стрима,

    так что терминальный оператор в цепочке может быть только один.

    У стрима может быть сколько угодно вызовов промежуточных операций и последним вызов конечной операции.

    При этом все промежуточные операции выполняются лениво и пока не будет вызвана конечная операция никаких действий на самом деле не происходит

    (похоже на создание объекта Thread или Runnable, без вызова start()).

    Стримы создаются на основе каких-либо источников, например классов из java.util.Collection.

    Операции над стримами могут выполняться как последовательно, так и параллельно.

    Потоки не могут быть использованы повторно. Как только была вызвана какая-нибудь конечная операция, поток закрывается.

    Ассоциативные массивы (maps), например, HashMap, не поддерживаются.

    Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных int, long и double: IntStream, LongStream и DoubleStream.

    Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями:
    используют специализированные лямбда-выражения, например, IntFunction или IntPredicate вместо Function и Predicate;

    поддерживают дополнительные конечные операции sum(), average(), mapToObj().


    1   2   3


    написать администратору сайта