Мой взгляд на выражения Lambda на Java довольно запутался:
Я думаю, что я думаю: выражения Lambda уменьшают опыт чтения программ Java. Программы Java никогда не были выдающимися в выразительности. Напротив, одним из факторов, который делает Java популярным, является ее безопасность и консерватизм-даже новички могут писать надежный и простой в порядок код, если они обращают на него внимание. Выражения Lambda имеют относительно более высокие требования для разработчиков, поэтому они также увеличивают некоторые трудности в техническом обслуживании.
Я думаю, что еще одна вещь: как код кода, необходимо изучить и принимать новые функции языка. Если вы отказываетесь от его выразительных сильных сторон только из -за его плохого опыта чтения, то некоторым людям трудно понять даже тринокулярные выражения. Язык также развивается, и те, кто не может не отставать, останутся добровольно.
Я не хочу оставаться позади. Однако, если бы мне пришлось сделать выбор, мое решение все еще было относительно консервативным: нет необходимости использовать лямбду на языке Java - это заставляет многих людей в нынешнем Java Circle не привыкнуть к нему, и вызовет увеличение затрат на рабочую силу. Если вам это очень нравится, вы можете рассмотреть возможность использования Scala.
В любом случае, я все еще начал пытаться освоить лямбду, в конце концов, некоторые коды, поддерживаемые на работе, используют лямбду (поверьте мне, я постепенно удаляю его). Учебные пособия, которые нужно изучать, являются связанными учебниками на официальном веб -сайте Oracle Java.
― - -
Предположим, что в настоящее время создается приложение социальной сети. Одна особенность заключается в том, что администраторы могут выполнять определенные действия на участников, которые соответствуют указанным критериям, таким как отправка сообщений. В следующей таблице подробно описывается этот вариант использования:
| Поле | описывать |
| имя | Действия для выполнения |
| Ключевые участники | администратор |
| Предварительные условия | Вход в систему администратора |
| Пост-кондиционер | Выполнять только действия для членов, которые соответствуют указанным критериям |
| Основной сценарий успеха | 1. Администратор устанавливает стандарты фильтрации для целевых членов для выполнения операции; 2. Администратор выбирает действие для выполнения; 3. Администратор нажимает кнопку отправки; 4. Система находит участников, которые соответствуют указанным критериям; 5. Система выполняет предварительно выбранные операции для участников, которые соответствуют указанным критериям. |
| Расширенный | Прежде чем выбрать операцию выполнения или перед нажатием кнопки «Отправить», администратор может выбрать, следует ли предварительно просмотреть информацию о членах, которая соответствует критериям фильтрации. |
| Частота возникновения | Это происходит много раз в день. |
Используйте следующий класс лица, чтобы представить информацию о членах в социальных сетях:
Public Class Person {public enum sex {male, женский} string name; Localdate День рождения; Половой пол; String emailAddress; public int getage () {// ...} public void printperson () {// ...}}Предположим, что все участники сохраняются в экземпляре <Person>.
В этом разделе мы начинаем с очень простого метода, затем пытаемся реализовать его с помощью локальных классов и анонимных классов, и в конце мы постепенно испытаем мощность и эффективность выражений Lambda. Полный код можно найти здесь.
Решение 1: Создайте методы для поиска членов, которые соответствуют указанным критериям один за другим
Это самое простое и самое грубое решение для реализации вышеупомянутых случаев: он должен создать несколько методов, и каждый метод проверяет критерий (например, возраст или пол). Следующий код проверяет, что возраст старше, чем указанное значение:
public static void printpersOnsOlderThan (list <derson> spit, int age) {for (person p: spit) {if (p.getage ()> = age) {p.printperson (); }}}Это очень хрупкое решение, и вполне вероятно, что приложение не будет работать из -за небольшого обновления. Если мы добавим новые переменные члена в класс человека или изменим алгоритм для измерения возраста в стандарте, нам необходимо переписать много кода, чтобы адаптироваться к этому изменению. Кроме того, ограничения здесь слишком жесткие. Например, что мы должны делать, если хотим печатать участников, которые моложе указанного значения? Добавить еще один новый метод Printpersonsyoungerthan? Это, очевидно, глупый метод.
Решение 2: создать более общий метод
Следующий метод более адаптируется, чем PrintpersonsOlderThan; Этот метод печатает информацию о участнике в указанной возрастной группе:
public static void printpersonswithinagerange (list <person> inst, int low, int high) {for (person p: spit) {if (low <= p.getage () && p.getage () <High) {p.printperson (); }}}Теперь есть новая идея: что мы должны делать, если мы хотим распечатать информацию о указанном полу или которая соответствует указанному полу и находится в пределах указанной возрастной группы? Что если мы скорректируем класс человека и добавим такие свойства, как дружба и географическое местоположение. Хотя подобные методы написания более универсальны, чем Printpersonsyoungerthan, написание метода для каждого возможного запроса также может привести к хрупкости в коде. Лучше поместить стандартный контрольный код в новый класс.
Решение 3: реализовать стандартную проверку в местном классе
Следующий метод печатает информацию о участнике, которая соответствует критериям поиска:
public static void printpersons (список <person> spite, teperson tester) {for (person p: spite) {if (tester.test (p)) {p.printperson (); }}}Тестер объекта Checkperso используется в программе для проверки каждого экземпляра в списке параметров списка. Если tester.test () возвращает true, будет выполнен метод printperson (). Чтобы установить критерии поиска, необходимо реализовать интерфейс Checkperson.
Следующий класс реализует контроль от контроля и предоставляет конкретную реализацию метода испытаний. Метод испытаний в этом классе фильтрует информацию о членстве, которое соответствует требованиям к военной службе в Соединенных Штатах: то есть мужской пол и возраст в возрасте от 18 до 25 лет.
Class CheckPersonElibibleForsElectiveService реализует CheckPerson {public Boolean Test (Person P) {return P.Gender == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25; }}Чтобы использовать этот класс, вам нужно создать экземпляр и запустить метод Printpersons:
Printpersons (список, новый CheckPersonElibibleForselectiveservice ());
Теперь код выглядит менее хрупким - нам не нужно переписать код из -за изменений в структуре класса человека. Тем не менее, здесь все еще есть дополнительный код: недавно определенный интерфейс, который определяет внутренний класс для каждого стандарта поиска в приложении.
Поскольку CheckPersonElibableForsElectiveService реализует интерфейс, можно использовать анонимный класс без определения внутреннего класса для каждого стандарта.
Решение 4: Используйте анонимные классы для реализации стандартной проверки
Одним из параметров в методе Printpersons, который называется ниже, является анонимный класс. Функция этого анонимного класса такая же, как и в классе CheckPersonElibableForselectiveservice в схеме 3: Все они фильтрованы с полом мужского пола и в возрасте от 18 до 25 лет.
printpersons (список, new ceckperson () {public boolean test (person p) {return p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25;}});Эта схема уменьшает объем кодировки, потому что больше нет необходимости создавать новые классы для каждой схемы поиска. Тем не менее, это все еще немного неудобно сделать: хотя интерфейс Checkperson имеет только один метод, реализованный анонимный класс по -прежнему немного словесно и громоздкий. В настоящее время вы можете использовать Lambda Expression для замены анонимных классов. Следующее объясняет, как использовать Lambda Expression для замены анонимных классов.
Решение 5: Используйте Lambda Expressions для реализации стандартной проверки
Интерфейс контроля - это функциональный интерфейс. Так называемый функциональный интерфейс относится к любому интерфейсу, который содержит только один абстрактный метод. (Функциональный интерфейс также может иметь несколько методов по умолчанию или статических методов). Поскольку в функциональном интерфейсе существует только один абстрактный метод, имя метода может быть пропущено при реализации метода этого функционального интерфейса. Чтобы реализовать эту идею, вы можете заменить анонимные выражения класса на Lambda выражения. В методе Printpersons переписано ниже, соответствующий код подчеркивается:
printpersons (список, (человек p) -> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25);
Здесь вы также можете использовать стандартный функциональный интерфейс для замены интерфейса Checkperson, тем самым дополнительно упрощая код.
Решение 6: Используйте стандартные функциональные интерфейсы в выражениях Lambda
Давайте посмотрим на интерфейс Checkperson:
Interface Checkperson {Boolean Test (Person P); }Это очень простой интерфейс. Поскольку существует только один абстрактный метод, это также функциональный интерфейс. Этот абстрактный метод принимает только один параметр и возвращает логическое значение. Этот абстрактный интерфейс настолько прост, что мы рассмотрим, необходимо ли определить такой интерфейс в приложении. В настоящее время вы можете рассмотреть вопрос о использовании стандартных функциональных интерфейсов, определенных JDK, и вы можете найти эти интерфейсы в пакете функции java.util.util.
В этом примере мы можем использовать интерфейс Predicate <t> для замены Checkperson. В этом интерфейсе есть метод логического теста (T T):
Интерфейс предикат <t> {boolean test (t t); }Интерфейс Predicate <t> является общим интерфейсом. Общий класс (или общий интерфейс) указывает один или несколько параметров типа, используя пару угловых кронштейнов (<>). В этом интерфейсе есть только один параметр типа. Когда вы объявляете или создаете создание общего класса, используя конкретный класс, вы получаете параметризованный класс. Например, параметризованный предикат класса <человека> похож на это:
Интерфейс предикат <derson> {boolean test (человек t); }В этом параметризованном классе существует метод, который согласуется с параметрами и возвратными значениями метода Checkperson.boolean Test (Person P). Следовательно, вы можете использовать интерфейс Predicate <t> для замены интерфейса Checkperson, как показано в следующем методе:
public static void printpersonswithpredicate (list <derson> ряд, предикат <derss> tester) {for (person p: spit) {if (tester.test (p)) {p.printperson (); }}}Затем используйте следующий код, чтобы фильтровать членов военной службы, как в плане 3:
printpersonswithpredicate (spite, p -> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25);
Вы заметили, что при использовании предиката <Person> в качестве типа параметра не указан явный тип параметра. Это не единственное место, где применяются выражения Lambda. Следующая схема приведет к большему использованию выражений Lambda.
Решение 7: Используйте выражения Lambda на протяжении всего приложения
Давайте посмотрим на метод PrintpersonsWithPredicate и рассмотрим, можете ли вы использовать Lambda выражения здесь:
public static void printpersonswithpredicate (list <derson> ряд, предикат <derss> tester) {for (person p: spit) {if (tester.test (p)) {p.printperson (); }}}В этом методе каждый экземпляр человека в списке проверяется с использованием тестера экземпляра «Предикат». Если экземпляр человека соответствует критериям проверки, определенным в тестере, будет запускается метод экземпляра PrintPerson.
В дополнение к запуска метода PrintPerson, экземпляры человека, которые соответствуют стандарту тестера, также могут выполнять другие методы. Вы можете рассмотреть возможность использования выражения лямбда, чтобы указать метод, который будет выполнен (я думаю, эта функция хороша, что решает проблему, что методы в Java не могут быть переданы в виде объектов). Теперь вам нужно выражение Lambda, аналогичное методу Printperson - выражение лямбда, которое требует только одного параметра и возвращает void. Помните одну вещь: чтобы использовать Lambda Expressions, вам нужно сначала реализовать функциональный интерфейс. В этом примере необходим функциональный интерфейс, который содержит только один абстрактный метод. Этот абстрактный метод имеет параметр типа человека и возвращается к void. Вы можете взглянуть на стандартного потребителя функционального интерфейса <T>, предоставленного JDK, который имеет абстрактный метод voidce (t t) просто соответствует этому требованию. В следующем коде используйте экземпляр потребителя <T>, чтобы вызвать метод принятия вместо p.printperson ():
public static void processpersons (List <Person> Sport, Predicate <cersonge> tester, Consumer <Person> block) {for (Person P: spit) {if (tester.test (p)) {block.accep (p); }}}Соответственно, вы можете использовать следующий код для фильтрации членов военного возраста:
Processpersons (Spite, P -> P.GetGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p -> p.printperson ());
Если мы хотим делать вещи, которые не просто печатают информацию о участниках, но и больше вещей, такие как проверка членства, получение контактной информации о членах и т. Д. На данный момент нам нужен функциональный интерфейс с методом возвращаемого значения. Стандартная функциональная функция интерфейса JDK <T, r> имеет подобный метод, подобный этому r Apply (t t). Следующий метод получает данные из Mapper параметра и выполняет поведение, указанное блоком параметров на этих данных:
public static void processpersOnsWithFunction (List <Dersion> SISTER, PREDICAT <ENTER> TESTER, FUNCTION <Person, String> Mapper, Consumer <string> block) {for (Person P: Spit) {if (tester.test (p)) {String Data = mapper.apply (p); block.accept (data); }}}Следующий код получает информацию по электронной почте всех членов военного возраста в списке и распечатывает ее:
ProcessPersOnsWithFunction (SISTER, P -> P.GetGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p -> p.getemailDress (), email -> system.out.println (электронное письмо));
Решение 8: используйте дженерики чаще
Давайте рассмотрим метод процессов. Ниже приведена общая версия этого метода. Новый метод требует большей допуска в типах параметров:
public static <x, y> void обработчики (итерабильный <x> источник, предикат <x> tester, function <x, y> mapper, conmerber <y> block) {for (x p: source) {if (tester.test (p)) {y data = mapper.apply (p); block.accept (data); }}}Чтобы распечатать информацию о членах для военной службы в нужном возрасте, вы можете позвонить в метод процессов, например, следующее:
Processelements (Spit, P -> P.GetGender () == Person.sex.male && p.getage ()> = 18 && p.getage () <= 25, p -> p.getemailAddress (), email -> System.out.println (электронная почта));
Во время процесса вызова метода выполняется следующее поведение:
Получить информацию о объекте из коллекции, в этом примере получить информацию о объекте человека из списка экземпляров сбора.
Фильтруя объекты, которые могут соответствовать тестированию экземпляра предиката. В этом примере объект Predicate представляет собой выражение Lambda, которое определяет условия для фильтрации военной службы в правильном возрасте.
Отфильтрованный объект передается на карту функции объекта для обработки, и Mapper будет соответствовать значению с этим объектом. В этом примере Mapper объекта функции является выражением Lambda, которое возвращает адрес электронной почты каждого члена.
Определяет поведение блоком объекта потребителя для значения, соответствующего Mapper. В этом примере объект потребителя является выражением Lambda, которое является функцией печати строки, которая является членом адреса электронной почты, возвращаемым Mapper экземпляра функции.
Решение 9: Используйте операцию агрегации, используя выражение Lambda в качестве параметра
Следующий код использует операцию агрегации для печати адресов электронной почты членов военного возраста в коллекции списка:
spite.stream () .filter (p -> p.getgender () == person.sex.male && p.getage ()> = 18 && p.getage () <= 25) .map (p -> p.getemailaddress ()) .foreach (email -> system.out.println (email));
Проанализируйте процесс выполнения приведенного выше кода и организуйте следующую таблицу:
Поведение | Операция агрегации |
Получите объект | Stream <e> Stream () |
Фильтруя объекты, которые соответствуют указанным критериям экземпляра предиката | Поток <t> фильтр (предикат <? Super t> предсказать) |
Получите соответствующее значение объекта через экземпляр функции | <r> Stream <r> map (function <? Super t ,? extends r> mapper) |
Выполнить поведение, указанное экземпляром потребителя | void foreach (потребитель <? Super t> действие) |
Операции фильтра, карты и форета в таблице - все совокупные операции. Элементы, обрабатываемые операцией агрегации, поступают из потока, не непосредственно из коллекции (то есть потому что первый метод, вызванный в этой программе примера, является Stream ()). Поток - это последовательность данных. В отличие от коллекций, поток не хранит данные с определенной структурой. Вместо этого Stream получает данные из определенного источника, например, от сбора, через конвейер. Pipeline-это последовательность эксплуатации потока, в этом примере фильтра-карты. Кроме того, операции агрегации обычно используют выражения Lambda в качестве параметров, что также дает нам много пользовательского пространства.