Зачем использовать выражения лямбды
Давайте посмотрим на несколько примеров:
Первый пример - выполнить задачу в отдельном потоке, который мы обычно реализуем следующим образом:
Работник класса реализует runnable {public void run () {for (int i = 0; i <100; i ++) dowork (); } ...} Рабочий w = new Worker (); новый поток (w) .start ();Вторым примером является метод сравнения настраиваемого строки (по длине строки), который обычно выполняется:
Class LengthComparator реализует компаратор <string> {public int compare (String First, String Second) {return integer.compare (first.length (), second.length ()); }} Arrays.sort (Strings, New LengthComparator ());В третьем примере, в Javafx, добавьте обратный вызов в кнопку:
button.setonAction (new EventHandler <actionEvent> () {public void handle (actionEvent event) {System.out.println ("Спасибо за нажатие!");}});Эти примеры имеют одну общую черту, то есть сначала определяют блок кода, передают его объекту или методу, а затем выполняют его. Перед выражением Lambda Java не разрешает прямое передачу кодовых блоков, поскольку Java ориентирована на объект, поэтому объект должен быть передан, чтобы инкапсулировать кодовый блок, который будет выполнен в объект.
Синтаксис экспрессии Lambda
Комппаратор длины во втором примере выше выражается как выражение Lambda:
(Строка сначала, String Second) -> integer.compare (first.length (), second.length ());
-> До списка параметров, за которым следует тело оператора выражения;
Если тело утверждения выражения составляет более одной строки, тело заявления записано в {}, как обычная функция:
(Строка сначала, String Second) -> {if (first.length ()> second.length ()) {return 1; } else if (first.length () == second.length ()) {return 0; } else {return -1; }};Если нет параметров, () все еще нужно доставить с собой. Например, первый пример выше может быть выражен как:
() -> {for (int i = 0; i <1000; i ++) {dowork (); }}Если тип параметра может быть автоматически выведен из контекста, вы можете опустить:
Comporator <String> comp = (первое, второе) // так же, как (String First, String Second) -> integer.compare (first.length (), second.length ());
Если есть только один параметр, и тип может быть автоматически выведен, Crackets () также можно пропустить:
// вместо (Event) -> или (Event Event) -> eventHandler <CeactEvent> alinger = event -> system.out.println ("Спасибо за нажатие!");Тип возвращаемого значения выражения Lambda автоматически выводится, поэтому его не нужно указать; При выражении Lambda некоторые условные ветви имеют возвратные значения, но другие ветви не имеют возвратных значений, которые не допускаются, например:
(x) -> {if (x> = 0) {return 1; }}Кроме того, разница между выражением Lambda и оператором Lambda заключается в том, что выражение Lambda не нужно писать ключевое слово return. Время выполнения Java вернет результат выражения как возвращаемого значения, в то время как оператор Lambda - это выражение, написанное в {}, и необходимо использовать ключевое слово возврата, например:
// выражение rambdacomparator <string> comp1 = (первое, второе) -> integer.compare (first.length (), second.length ()); // оператор lambdacomparator <string> comp2 = (первое, второе) -> {return integer.compare (first.length (), second.length ());}; Функциональный интерфейс
Если интерфейс имеет только один абстрактный метод, он называется
Функциональный интерфейс, такой как бегающий, компаратор и т. Д.
В любом месте, где необходим объект функционального интерфейса, вы можете использовать Lambda Expressions:
Arrays.sort (слова, (первое, второе) -> integer.compare (first.length (), second.length ()));
Здесь второй параметр () требует объекта компаратора, а компаратор
Функциональный интерфейс, поэтому вы можете напрямую передать выражение Lambda. При вызове метода compare () объекта он должен выполнить тело оператора в выражении Lambda;
Если утверждение выражения лямбда выбрасывает исключение, соответствующий абстрактный метод в функциональном интерфейсе должен бросить исключение, в противном случае необходимо явно поймать исключение в выражении Lambda:
Runnable r = ()-> {System.out.println ("-------"); try {thread.sleep (10); } catch (прерывание Exception e) {// catch Exception}}; Callable <string> c = ()-> {System.out.println ("----------"); Thread.sleep (10); возвращаться "";}; Ссылка на метод
Если параметры выражения Lambda передаются в виде параметров методу, а их эффект выполнения одинаково, выражение лямбда может быть выражено с использованием ссылки на метод, а следующие два метода эквивалентны:
(x) -> System.out.println (x) System.out :: println
Среди них System.out :: println называется ссылкой на метод.
Справочник по методу в основном входит в три формы:
Для первых двух методов соответствующие параметры выражения Lambda и параметры метода одинаковы, например:
System.out :: println (x) -> system.out.println (x) math :: pow (x, y) -> math.pow (x, y)
Для третьего метода, в соответствующем корпусе оператора выражения Lambda, первый параметр используется в качестве объекта, вызывается метод, а другие параметры используются в качестве параметров метода, такие как:
String :: CompareToIgnoreCase (S1, S2) -> S1.com PparetoIgnoreCase (S2) 1.5 Ссылка на конструктор
Ссылка на конструктор аналогична ссылке на метод, но это специальный метод: новый. Конкретный конструктор определяется контекстной средой, такой как:
List <string> labels = ...; Stream <Tood> stream = labels.stream (). Map (button :: new);
Кнопка :: новая эквивалентна (x) -> кнопки (x), поэтому конструктор называется: кнопка (x);
В дополнение к созданию одного объекта, вы также можете создать множество объектов, таких как следующие два эквивалента:
int [] :: new (x) -> new int [x]
Переменная область
Lambd выражений захватывает переменные, доступные в текущей области, например:
public void repeatmessage (строковый текст, int count) {runnable r = () -> {for (int i = 0; i <count; i ++) {System.out.println (text); Thread.yield (); }}; Новый поток (r) .start ();}Но эти переменные должны быть неизменными, почему? См. Следующий пример:
int mockes = 0; for (path P: files) new Thread (() -> {if (p имеет какое -то свойство) соответствует ++;}). start (); // незаконные, чтобы мутировать матчиПоскольку изменяемые переменные не защищены потоком в выражениях Lambda, это согласуется с требованиями внутренних классов, и только извне определенные конечные переменные можно ссылаться во внутренних классах;
Объем выражения лямбда такой же, как и у вложенного блока кода, поэтому имя параметра или имя переменной в выражении лямбда не может противоречить локальным переменным, например:
Path First = paths.get ("/usr/bin"); Comporator <string> comp = (первое, второе) -> integer.compare (first.length (), second.length ()); // ошибка: переменная сначала уже определенаЕсли на эту переменную ссылаются в выражении лямбды, эта ссылка является этой переменной метода, которая создает выражение лямбды, например:
public class application () {public void dowork () {Runnable Runner = () -> {...; System.out.println (this.toString ()); ...}; }} Итак, здесь это. ToString () вызывает toString () объекта приложения, а не выполняется
объект.
Метод по умолчанию
В интерфейсе могут быть только абстрактные методы. Если новый метод добавляется в существующий интерфейс, все классы реализации интерфейса должны реализовать этот метод.
Java 8 представляет концепцию метода по умолчанию и добавляет метод по умолчанию к интерфейсу, который не уничтожит существующие правила интерфейса. Класс реализации интерфейса может выбрать переопределение или непосредственно наследовать метод по умолчанию, например:
Интерфейс человек {long getId (); по умолчанию string getName () {return "John Q. public"; }}Java допускает множественное наследство. Как справиться с этим конфликтом, если методы, определенные в родительском классе класса, точно такие же, как методы по умолчанию, определенные в интерфейсе, или два интерфейса класса точно одинаковы, как справиться с этим конфликтом? Правила обработки следующие:
Если метод конфликтует между родительским классом и интерфейсом: преобладают методы в родительском классе, а методы в границе раздела должны быть проигнорированы;
Если метод по умолчанию в конфликтах двух интерфейсов, вам необходимо переопределить метод для разрешения конфликта;
Статический метод
Перед Java 8 только статические переменные могут быть определены на интерфейсе. Начиная с Java 8, статические методы могут быть добавлены в интерфейс, например, как
Интерфейс компаратора добавил серию статических методов сравнения XXX, таких как:
public static <t> компаратор <t> comparingInt (tointFunction <? Super T> KeyExtractor) {objects.Requirenonnull (keyExtractor); return (компаратор <t> & serializable) (C1, C2) -> integer.compare (keyextractor.applyasint (c1), keyextractor.applyasint (c2));}Используя этот статический метод, следующие два метода также эквивалентны:
1
Arrays.sort (города, (первое, второе) -> integer.compare (first.length (), second.length ()));
2
Arrays.sort (Cities, Comparator.comParingInt (String :: Length));
Поэтому, когда мы разрабатываем наши собственные интерфейсы в будущем, нам больше не нужно определять отдельные классы инструментов (такие как коллекции/коллекция).
Просто используйте статический метод в интерфейсе.
Анонимный внутренний класс
В мире Java анонимные внутренние классы могут реализовать операции, которые могут выполняться только один раз в приложении. Например, в приложении Android обрабатывается событие нажатия кнопки. Вам не нужно писать отдельный класс, чтобы справиться с событием Click, вы можете сделать это с помощью анонимного внутреннего класса:
Кнопка кнопки = (кнопка) findViewById (r.id.button1); button.setOnclickListener (new OnClickListener () {@Override public void onclick (view) {toast.maketext (mainActivity.this, «кнопка нажата», toast.length_short) .show ();}}); Lambda Пример 1. Runnable Lambda Давайте посмотрим на несколько примеров. Вот пример запуска: public void runnabletest () {System.out.println ("=== runnabletest ==="); // анонимный Runnable Runnable r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // Lambda Runnable Runnable r2 = () -> System.out.println («Привет, мир два!»); // выполнить две функции выполнения r1.run (); r2.run (); } public void runnabletest () {System.out.println ("=== runnabletest ==="); // анонимный Runnable Runnable r1 = new Runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // Lambda Runnable Runnable r2 = () -> System.out.println («Привет, мир два!»); // выполнить две функции выполнения r1.run (); r2.run (); } Ни реализация, ни возвращаемая стоимость не возвращаются. Запускаемые выражения Lambda Используйте кодовые блоки, чтобы упростить код из пяти элементов в один оператор. Public Class Person {Private String Givenname; частная строковая фамилия; частный int возраст; частный гендерный пол; Приватная строковая электронная почта; Частный строковый телефон; частный строковый адрес;} Public Class Person {Private String Givenname; частная строковая фамилия; частный int возраст; частный гендерный пол; Приватная строковая электронная почта; Частный строковый телефон; частный строковый адрес;} Ниже приведено, как реализовать интерфейс компаратора с использованием анонимных внутренних классов и выражений Lambda: открытый класс ComparatorTest {public static void main (string [] args) {list <personlist = person.createShortlist (); // Использование внутреннего класса для реализации Sorting Collections.sort (PersonList, New Comparator <Person> () {public int compare (person p1, person p2) {return p1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== Сортированная фамилия ASC ==="); для (Person P: PersonList) {p.printname (); } // реализация с использованием Lambda Expression // Accending System.out.println ("=== Сортированная фабрика ASC ==="); Collections.sort (PersonList, (Person P1, Person P2) -> p1.getSurname (). Compareto (p2.getSurname ())); для (Person P: PersonList) {p.printname (); } // desc Secreding Sevelods.out.println ("=== Сортированная фамилия DECS ==="); Collections.sort (PersonList, (P1, P2) -> p2.getSurname (). Compareto (p1.getSurname ())); для (Person P: PersonList) {p.printname (); }}} открытый класс ComparatorTest {public static void main (string [] args) {list <personlist = person.createShortlist (); // Использование внутреннего класса для реализации Sorting Collections.sort (PersonList, New Comparator <Person> () {public int compare (person p1, person p2) {return p1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== Сортированная фамилия ASC ==="); для (Person P: PersonList) {p.printname (); } // реализация с использованием Lambda Expression // Accending System.out.println ("=== Сортированная фабрика ASC ==="); Collections.sort (PersonList, (Person P1, Person P2) -> p1.getSurname (). Compareto (p2.getSurname ())); для (Person P: PersonList) {p.printname (); } // desc Secreding Sevelods.out.println ("=== Сортированная фамилия DECS ==="); Collections.sort (PersonList, (P1, P2) -> p2.getSurname (). Compareto (p1.getSurname ())); для (Person P: PersonList) {p.printname (); }}} Вы можете видеть, что анонимные внутренние классы могут быть реализованы с помощью выражений Lambda. Обратите внимание, что первое выражение лямбды определяет тип параметра как человека; Второе выражение Lambda пропускает определение типа. Lambda Expressions Support Type Nockdown, и если необходимый тип может быть выведен через контекст, определение типа может быть опущено. Здесь, поскольку мы используем Lambda Expressions в компараторе, который использует общие определения, компилятор может вывести эти два параметра в качестве человека.