билеты модуль 7. 1 Функциональные интерфейсы, основные типы
Скачать 184 Kb.
|
1) Функциональные интерфейсы, основные типы. Интерфейс называется функциональным если у него есть ровно один абстрактный метод проверить это можно аннатацией @Functinalinterface 1) [Consumer - потребители, те кто принимают но не возвращают в замен. Его подвиды IntConsumer, LongConsumer, DoubleConsumer они есть потому, что дженерики не могут парметризоваться примитивами. 2) [Supplir - поставщики, они не принимают ни какое значение, а просто возвращают какое-то значение. 3) [Predicate - приниает выражение какого то типа а наружу выдает булевское значение. есть для примитивов, есть булевские 4) [Function - принимает аргумент и возвращает значение какого то типа, в общем случае эти типы разные. Бывает принимают два параметра. могут стоять примитивы 5) [Operator - это частный случай функции, когда на вход подается значение одного и того же типа получаем на выходе. Унарный оператор принимает один параметр а Бинарный два параметра отдельные интерфейсы над лонгами и даблами 2) Что такое лямбда выражение и ссылка на метод. Как они связаны с анонимным классом Лямбда это возможность замены вызова реализации интерфейса с одним методом. Лямбдой выражением мы просто более компактно записываем, вызов метода функционального интерфейса с одним методом [Лямбда представляет набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы. Основу лямбда-выражения составляет лямбда-оператор, который представляет стрелку ->. Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения, а правая собственно представляет тело лямбда-выражения, где выполняются все действия. Лямбда-[выражение, образует реализацию метода, определенного в функциональном интерфейсе. По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java. Если существующий в классе метод уже делает все, что необходимо, то можно воспользоваться механизмом method [reference (ссылка на метод) для непосредственной передачи этого метода. на 1) статический метод; имя_класса[::имя_статического_метода для статического метода; 2) на метод экземпляра; объект_класса::имя_метода для метода экземпляра; 3) на конструкторе. название_класса::new для конструктора. Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод. !!! из Лямбды нельзя изменять переменные содержащиеся в ее методе. трюк с массивом единичной длины !!!В Java 8 можно любой анонимный класс, который реализует один абстрактный метод заменить на лямбду выражение, если анонимный класс реализует два абстрактных метода, то заменить на лямбду будет невозможно. 3) Что такое [Стрим. [Stream (последовательными (sequential) и параллельными (parallel), конечные и бесконечные) Стрим это последовательность элементов, потенциально бесконечная, с возможностью применять к ней, сложные, поэтапные преобразования без цикла и условного оператора. И программа будет иметь, простую линейную структуру.(Владыкин) Стрим состаит из источника - (элементы) -> промежуточные операторв - (элементы) -> терминальный оператор !!! Важно обработка происходит от терминального источника Операторы можно разделить на две группы: [Промежуточные или [КОНВЕЕРНЫЕ операторы(“[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(). 4) Какие существуют способы [создания стрима? 1) Из коллекции: Stream Стрим из List: list.stream() Стрим из Map: map.entrySet().stream() 2) Из набора значений: Stream 3) Из массива: Stream 4) Из файла (каждая строка в файле будет отдельным элементом в стриме): Stream 5) Из строки: IntStream fromString = "0123456789".chars(); 6) С помощью Stream.builder(): Stream 7) С помощью Stream.iterate() (бесконечный): Stream 8) С помощью Stream.generate() (бесконечный): Stream 9) Создание паралельного стрима collection.parallelStream() Stream 5) Какие [бывают стримы (По разным критериям, например "конечные и бесконечные") Стримы бывают ПОСЛЕДОВАТЕЛЬНЫМИ (sequential) и ПАРАЛЕЛЬНЫМИ (parallel). Последовательные выполняются только в текущем потоке, а вот параллельные используют общий пул ForkJoinPool.commonPool(). При этом элементы разбиваются (если это возможно) на несколько групп и обрабатываются в каждом потоке отдельно. Затем на нужном этапе группы объединяются в одну для предоставления конечного результата. Чтобы получить параллельный стрим, нужно либо вызвать метод parallelStream() вместо stream(), либо превратить обычный стрим в параллельный, вызвав промежуточный оператор [parallel. Кроме объектных стримов Stream - IntStream для int, - LongStream для long, - DoubleStream для double 6) Терминальные и промежуточные методы. ПРОМЕЖУТОЧНЫЕ ОПЕРАТОРЫ конвеерные [filter Отфильтровывает записи, возвращает только записи, соответствующие условию collection.stream().filter(«a1»::equals).count() [skip Позволяет пропустить N первых элементов collection.stream().skip(collection.size() — 1).findFirst().orElse(«1») [distinct Возвращает стрим без дубликатов (для метода equals) collection.stream().distinct().collect(Collectors.toList()) [map Преобразует каждый элемент стрима collection.stream().map((s) -> s + "_1").collect(Collectors.toList()) [peek Возвращает тот же стрим, но применяет функцию к каждому элементу стрима collection.stream().map(String::toUpperCase).peek((e) -> System.out.print("," + e)). collect(Collectors.toList()) [limit Позволяет ограничить выборку определенным количеством первых элементов collection.stream().limit(2).collect(Collectors.toList()) [sorted Позволяет сортировать значения либо в натуральном порядке, либо задавая Comparator collection.stream().sorted().collect(Collectors.toList()) [mapToInt, mapToDouble, mapToLong Аналог map, но возвращает числовой стрим (то есть стрим из числовых примитивов) collection.stream().mapToInt((s) -> Integer.parseInt(s)).toArray() [flatMap, [flatMapToInt, [flatMapToDouble, [flatMapToLong Похоже на map, но может создавать из одного элемента несколько collection.stream().flatMap((p) -> Arrays.asList(p.split(",")).stream()).toArray(String[]::new) [ТЕРМИНАЛЬНЫЕ ОПЕРАТОРЫ [findFirst Возвращает первый элемент из стрима (возвращает Optional) collection.stream().findFirst().orElse(«1») [findAny Возвращает любой подходящий элемент из стрима (возвращает Optional) collection.stream().findAny().orElse(«1») [collect Представление результатов в виде коллекций и других структур данных collection.stream().filter((s) -> s.contains(«1»)).collect(Collectors.toList()) [count Возвращает количество элементов в стриме collection.stream().filter(«a1»::equals).count() [anyMatch Возвращает true, если условие выполняется хотя бы для одного элемента collection.stream().anyMatch(«a1»::equals) [noneMatch Возвращает true, если условие не выполняется ни для одного элемента collection.stream().noneMatch(«a8»::equals) [allMatch Возвращает true, если условие выполняется для всех элементов collection.stream().allMatch((s) -> s.contains(«1»)) [min Возвращает минимальный элемент, в качестве условия использует компаратор collection.stream().min(String::compareTo).get() [max Возвращает максимальный элемент, в качестве условия использует компаратор collection.stream().max(String::compareTo).get() [forEach Применяет функцию к каждому объекту стрима, порядок при параллельном выполнении не гарантируется set.stream().forEach((p) -> p.append("_1")); [forEachOrdered Применяет функцию к каждому объекту стрима, сохранение порядка элементов гарантирует list.stream().forEachOrdered((p) -> p.append("_new")); [toArray Возвращает массив значений стрима collection.stream().map(String::toUpperCase).toArray(String[]::new); [reduce Позволяет выполнять агрегатные функции на всей коллекцией и возвращать один результат collection.stream().reduce((s1, s2) -> s1 + s2).orElse(0) Обратите внимание методы findFirst, findAny, anyMatch это short-circuiting методы, то есть обход стримов организуется таким образом чтобы найти подходящий элемент максимально быстро, а не обходить весь изначальный стрим. Краткое описание терминальных методов работы со стримами findFirst Возвращает первый элемент из стрима (возвращает Optional) collection.stream().findFirst().orElse(«1») findAny Возвращает любой подходящий элемент из стрима (возвращает Optional) collection.stream().findAny().orElse(«1») collect Представление результатов в виде коллекций и других структур данных collection.stream().filter((s) -> s.contains(«1»)).collect(Collectors.toList()) 7) Способы получения стрима. 8) Может ли функциональный интерфейс содержать что-то кроме абстрактного метода? функциональный интерфейс может содержать так же default и static методы. Такое решение исходит из понятия функции. Формально функция имеет имя, список параметров и возвращаемое значение. Если функция имеет тоже имя и другой список параметров или другое возвращаемое значение, то это другая функция. Значит логично на одну функцию иметь один экземпляр интерфейса с одним методом (ведь функцию можно только применить, и ничего более). В языке Java нет функций, как независимых сущностей, поэтому их эмулируют путем использования интерфейсов с одним абстрактным методом (Single Abstract Method, SAM). 9) Может ли стрим использоваться повторно? Потоки в Java 8 не могут быть использованы повторно. Как только вы называете какую-нибудь терминальную операция, поток закрывается Чтобы избежать этого, мы должны создать новую цепь для каждой терминальной операции. Supplier () -> Stream.of("dd2", "aa2", "bb1", "bb3", "cc") .filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); // операция пройдет успешно streamSupplier.get().noneMatch(s -> true); // здесь также все будет ok Каждый вызов конструктора get() создает новый поток, с которым мы можем безопасно работать. 10) Что такое функциональное программирование? [фп [функциональное программирование (декларативный (не императивный)) — это парадигма декларативного программирования, в которой программы создаются путем последовательного применения функций, а не инструкций. Каждая из этих функций принимает входное значение и возвращает согласующееся с ним выходное значение, не изменяясь и не подвергаясь воздействию со стороны состояния программы. Для таких функций предусмотрено выполнение только одной операции, если же требуется реализовать сложный процесс, то используется уже композиция функций, связанных последовательно. В процессе ФП мы создаем код, состоящий из множества модулей, поскольку функции в нем могут повторно использоваться в разных частях программы путем вызова, передачи в качестве параметров или возвращения. 11) Какие бывают стримы? 1)последовательные и паралельные, конечные и [бесконечные 12) Перечислить 3 терминальные операции. Их предназначение. 13) В чем разница между Comparable и Comparator? [Comparable обеспечивает единую последовательность сортировки. [Comparator предоставляет несколько последовательностей сортировки. Другими словами, мы можем отсортировать коллекцию на основе одного элемента, Другими словами, мы можем отсортировать коллекцию по нескольким элементам, таким как идентификатор, имя, цена и т. д. такого как идентификатор, имя и цена. Comparable влияет на исходный класс, т.е. модифицируется фактический класс. Comparator не влияет на исходный класс, т.е. фактический класс не изменяется. Comparable предоставляет метод compareTo() для сортировки элементов. Comparator предоставляет метод [compare() для сортировки элементов. Comparable присутствует в пакете java.lang. Comparator присутствует в пакете java.util. Мы можем отсортировать элементы списка типа Comparable методом [Collections.sort(List). Мы можем отсортировать элементы списка типа Comparator методом Collections.sort(List, Comparator). Comparable - имплементится самим классом, когда нужен натуральный порядок сортировки. Пример - класс String. Comparator - имплементится другими классами. Дает возможность отделить реализацию сравнения от класса и сделать несколько реализаций сравнения по разным параметрам для одного класса. 14)Что такое метод [референс [reference ? Ссылки на метод Если лямбда выражения вызывают только один существующий метод, лучше ссылать на этот метод по его имени. Ссылки на методы (Method References) – это компактные лямбда выражения для методов у которых уже есть имя. Например: Ссылка на статический метод ContainingClass::staticMethodName Function System.out.println(function.apply("TRUE")); Перепишем с помощью ссылки: Function System.out.println(function.apply("TRUE")); Ссылка на нестатический метод конкретного объекта containingObject::instanceMethodName Этот тип используется когда лямбда выражение вызывает метод внешнего уже существующего объекта. Consumer consumer.accept("OCPJP 8"); Перепишем, используя ссылку: Consumer consumer.accept("OCPJP 8"); Еще один пример: Integer integer = 5; Supplier System.out.println(supplier.get()); Перепишем: Integer integer = 5; Supplier System.out.println(supplier.get()); Ссылка на нестатический метод любого объекта конкретного типа ContainingType::methodName Function System.out.println(function.apply("OCPJP 8")); Перепишем: Function System.out.println(function.apply("OCPJP 8")); Ссылка на конструктор ClassName::new ClassName не может быть абстрактным классом или интерфейсом. Function System.out.println(function.apply("4")); Перепишем: Function System.out.println(function.apply("4")); 15) Может ли лямбда-выражение быть в несколько строк? да может, они должны быть обернуты в скобки |