«Ява все еще не мертва - и люди начинают это выяснять».
В этом уроке будет использоваться простой аннотированный код для описания новых функций, и вы не увидите текст блокбастера.
1. Метод по умолчанию интерфейса
Java 8 позволяет нам добавить реализацию метода неабстрора в интерфейс, просто используйте ключевое слово по умолчанию.
Кода -копия выглядит следующим образом:
Формула интерфейса {
двойной расчет (int a);
по умолчанию двойной sqrt (int a) {
вернуть math.sqrt (a);
}
}
В дополнение к вычисленному методу, интерфейс формулы также определяет метод SQRT.
Кода -копия выглядит следующим образом:
Формула Формула = Новая формула () {
@Override
Public Double рассчитайте (int a) {
вернуть SQRT (a * 100);
}
};
Formula.calculate (100);
Formula.sqrt (16);
Формула в статье реализована как экземпляр анонимного класса. В следующем разделе мы увидим более простой подход к реализации интерфейса с одним методом.
Примечание переводчика: в Java есть только единое наследство. На других языках метод получения класса имеет другой многоразовый код одновременно называется Mixin. Это новое специальное Java 8 ближе к черте Scala с точки зрения реализации компилятора. Существует также концепция, называемая методом расширения в C#, которая позволяет расширить существующие типы, что семантически отличается от этого в Java 8.
2. Lambda Expressions
Во -первых, давайте посмотрим, как строки расположены в старой версии Java:
Кода -копия выглядит следующим образом:
List <string> names = arrays.aslist ("peter", "anna", "mike", "xenia");
Collections.sort (имена, новый компаратор <string> () {
@Override
public int compare (string a, string b) {
вернуть b.compareto (a);
}
});
Просто передайте объект списка и компаратору в статические сборы методов. Скору, чтобы расположить его в указанном порядке. Обычно это делается для создания анонимного объекта компаратора и передать его методу.
В Java 8 вам не нужно использовать этот традиционный метод анонимных объектов.
Кода -копия выглядит следующим образом:
Collections.sort (names, (String a, String b) -> {
вернуть b.compareto (a);
});
Смотрите, код становится более сегментированным и читабельным, но на самом деле он может быть написан короче:
Кода -копия выглядит следующим образом:
Collections.sort (имена, (строка A, String B) -> b.compareto (a));
Для корпуса функции только с одной строкой кода вы можете удалить скобки {} и вернуть ключевые слова, но вы можете написать их короче:
Кода -копия выглядит следующим образом:
Collections.sort (имена, (a, b) -> b.compareto (a));
Компилятор Java может автоматически вывести типы параметров, поэтому вам не нужно снова писать тип. Далее, давайте посмотрим, что могут сделать более удобные выражения Lambda:
3. Функциональный интерфейс
Как Lambda выражения представлены в системе типа Java? Каждое выражение Lambda соответствует типу, обычно типу интерфейса. «Функциональный интерфейс» относится к интерфейсу, который содержит только абстрактный метод, и каждое выражение лямбда этого типа будет соответствовать этому абстрактному методу. Поскольку метод по умолчанию не является абстрактным методом, вы также можете добавить методы по умолчанию в свой функциональный интерфейс.
Мы можем рассматривать Lambda Expressions как любой тип интерфейса, который содержит только один абстрактный метод, чтобы убедиться, что ваш интерфейс должен соответствовать этому требованию. Аннотация, компилятор обнаружит, что интерфейс, на который вы отмечены, аннотация.
Примеры следующие:
Кода -копия выглядит следующим образом:
@FunctionalInterface
Интерфейс преобразователь <f, t> {
T конвертируйте (f из);
}
Преобразователь <string, integer> converter = (from) -> integer.valueof (from);
Integer преобразовано = converter.convert ("123");
System.out.println (преобразован);
Следует отметить, что если @functionalInterface не указан, приведенный выше код также является правильным.
Переводчики карты Lambda выражения на интерфейс с одним методом. Функция. Интерпретатор Rhino автоматически сделает экземпляр одного интерфейса для адаптера функции.
4. Справочник по методу и конструктору
Код в предыдущем разделе также может быть представлен ссылками на статический метод:
Кода -копия выглядит следующим образом:
Преобразователь <string, integer> converter = integer :: valueof;
Integer преобразовано = converter.convert ("123");
System.out.println (преобразован);
Java 8 позволяет использовать ключевое слово :: для передачи метода или конструктора.
Кода -копия выглядит следующим образом:
конвертер = что -то :: startSwith;
String converted = converter.convert ("java");
System.out.println (преобразован);
Давайте посмотрим на то, как конструкторы ссылаются с помощью ключевого слова :: мы определяем простой класс с несколькими конструкторами:
Кода -копия выглядит следующим образом:
классный человек {
String FirstName;
String Lastname;
Человек() {}
Человек (String FirstName, String Lastname) {
this.firstname = FirstName;
this.lastName = lastName;
}
}
Далее мы указываем интерфейс фабрики объекта для создания объектов человека:
Кода -копия выглядит следующим образом:
Интерфейс PersonFactory <P Extends Person> {
P create (String FirstName, String Lastname);
}
Здесь мы используем конструктор -ссылки, чтобы ассоциировать их вместо реализации полной фабрики:
Кода -копия выглядит следующим образом:
PersonFactory <Personfactory = person :: new;
Person Person = PersonFactory.Create («Питер», «Паркер»);
Нам нужно использовать только Person :: Новое, чтобы получить ссылку на конструктор класса человека.
5. Lambda Scope
Способ получить доступ к внешним прицелам в выражениях Lambda аналогична тому, что в более старых версиях анонимных объектов. Вы можете непосредственно получить доступ к внешним локальным переменным, отмеченным окончательными, или полями и статическими переменными экземпляра.
6. Доступ к локальным переменным
Мы можем получить доступ к внешним локальным переменным непосредственно в выражениях Lambda:
Кода -копия выглядит следующим образом:
окончательный int num = 1;
Converter <Integer, String> StringConverter =
(от) -> string.valueof (от + num);
StringConverter.convert (2);
Но в отличие от анонимных объектов, переменная num здесь может быть объявлена окончательной, не объявив его окончательным, а код также правильный:
Кода -копия выглядит следующим образом:
int num = 1;
Converter <Integer, String> StringConverter =
(от) -> string.valueof (от + num);
StringConverter.convert (2);
Однако NUM здесь не должно быть изменено последующим кодом (то есть неявно имеет окончательную семантику), например, следующее нельзя составить:
Кода -копия выглядит следующим образом:
int num = 1;
Converter <Integer, String> StringConverter =
(от) -> string.valueof (от + num);
num = 3;
Попытка изменить NUM в Lambda Expressions также не допускается.
7. Полевые поля объектов и статические переменные
В отличие от локальных переменных, поля и статические переменные в Lambda могут быть прочитаны и написаны. Такое поведение согласуется с анонимными объектами:
Скопируйте код следующим образом: класс Lambda4 {
статический int outterstaticnum;
int outernum;
void testscopes () {
Converter <Integer, String> StringConverter1 = (из) -> {
outernum = 23;
return string.valueof (от);
};
Converter <Integer, String> StringConverter2 = (из) -> {
overstaticnum = 72;
return string.valueof (от);
};
}
}
8. Метод по умолчанию для доступа к интерфейсу
Помните пример формулы в первом разделе.
Метод по умолчанию не может быть доступен в выражении Lambda, и следующий код не будет составлен:
Кода -копия выглядит следующим образом:
Формула формула = (a) -> sqrt (a * 100);
Встроенные функциональные интерфейсы
API JDK 1.8 содержит множество встроенных функциональных интерфейсов, таких как компаратор или бегущие интерфейсы, обычно используемые в старой Java, и эти интерфейсы добавили @functionalInterface Annotation для использования на лямбдах.
Java 8 API также предоставляет много новых функциональных интерфейсов, чтобы сделать работу более удобной.
Предикат интерфейс
Интерфейс предиката имеет только один параметр, который возвращает логический тип. Этот интерфейс содержит ряд методов по умолчанию, чтобы объединить предикат в другую сложную логику (например: против или, не):
Кода -копия выглядит следующим образом:
Predicate <string> predicate = (s) -> s.length ()> 0;
Прогноз.test ("foo");
Прогноз.negate (). Test ("foo");
Предикат <boolean> nonnull = Objects :: nonnull;
Предикат <boolean> isnull = Objects :: isnull;
Predicate <string> isempty = string :: isempty;
Predicate <string> isNotempty = isempty.negate ();
Функциональный интерфейс
Интерфейс функции имеет параметр и возвращает результат и поставляется с некоторыми методами по умолчанию (Compose и Then), которые можно объединить с другими функциями:
Кода -копия выглядит следующим образом:
Функция <string, integer> tointeger = integer :: valueof;
Function <String, String> BacktoString = tointeger.andThen (string :: valueOf);
BacktoString.Apply ("123");
Интерфейс поставщика
Интерфейс поставщика возвращает значение любого типа.
Кода -копия выглядит следующим образом:
Поставщик <Person> personsupplier = person :: new;
personsupplier.get (); // Новый человек
Потребительский интерфейс
Интерфейс потребителя представляет операцию, выполняемую на одном параметре.
Кода -копия выглядит следующим образом:
Потребитель <Derson> Greeter = (p) -> System.out.println ("Привет," + p.firstname);
greeter.accept (новый человек ("Люк", "Скайуокер"));
Интерфейс компаратора
Кораптор - это классический интерфейс в старой Java, а Java 8 добавила на него множество методов по умолчанию:
Кода -копия выглядит следующим образом:
Comporator <Person> компаратор = (P1, P2) -> p1.firstname.compareto (p2.firstname);
Человек P1 = новый человек («Джон», «Доу»);
Человек P2 = новый человек («Алиса», «Страна чудес»);
Comporator.compare (P1, P2);
Comporator.Reversed (). Сравнение (P1, P2);
Дополнительный интерфейс
Необязательно - это не функция, а интерфейс.
Необязательно определяется как простой контейнер, значение которого может быть нулевым или нет. Перед Java 8 функция, как правило, должна возвращать непустые объекты, но иногда она может возвращать NULL.
Кода -копия выглядит следующим образом:
Необязательный <string> необязательный = необязательный.of ("bam");
Необязательно
Необходимый.get ();
Необязательный.
Необязательный.
Потоковой интерфейс
java.util.stream представляет собой последовательность операций, которые могут быть применены к набору элементов одновременно. Операции потока разделены на два типа: промежуточные операции или окончательные операции. Создание потока требует указания источника данных, такого как подкласс java.util.collection, List или Set, который не поддерживается MAP. Операции потока могут быть выполнены последовательно или параллельно.
Во -первых, давайте посмотрим, как используется поток.
Кода -копия выглядит следующим образом:
Список <string> stringCollection = new ArrayList <> ();
stringCollection.add ("ddd2");
StringCollection.Add ("AAA2");
stringCollection.add ("bbb1");
StringCollection.Add ("AAA1");
stringCollection.add ("bbb3");
stringCollection.add ("ccc");
stringCollection.add ("bbb2");
stringCollection.add ("ddd1");
Java 8 расширяет класс коллекции и может создать потоки через collection.stream () или collection.parallelstream (). В следующих разделах подробно объясняются широко используемые операции потока:
Фильтр фильтра
Фильтрация фильтруется через предикатный интерфейс, и только элементы, которые соответствуют критериям, хранятся. Foreach требует функции для выполнения фильтрованных элементов в последовательности. Foreach - это последняя операция, поэтому мы не можем выполнить другие потоковые операции после Foreach.
Кода -копия выглядит следующим образом:
StringCollection
.транслировать()
.filter ((s) -> s.startswith ("a"))
.foreach (System.out :: println);
// "AAA2", "AAA1"
Сортировка сортировки
Сортировка - это промежуточная операция, и возвращаемый поток после сортировки возвращается. Если вы не указали пользовательский компаратор, будет использоваться сортировка по умолчанию.
Кода -копия выглядит следующим образом:
StringCollection
.транслировать()
.sorted ()
.filter ((s) -> s.startswith ("a"))
.foreach (System.out :: println);
// "AAA1", "AAA2"
Следует отметить, что сортировка создает только расположенный поток и не повлияет на исходный источник данных.
Кода -копия выглядит следующим образом:
System.out.println (StringCollection);
// DDD2, AAA2, BBB1, AAA1, BBB3, CCC, BBB2, DDD1
Картирование карты
Карта промежуточной работы преобразует элементы в дополнительные объекты в последовательности в соответствии с указанным интерфейсом функции. Вы также можете использовать карту для преобразования объектов в другие типы.
Кода -копия выглядит следующим образом:
StringCollection
.транслировать()
.map (string :: touppercase)
.sorted ((a, b) -> b.compareto (a))
.foreach (System.out :: println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Соответствовать
Поток обеспечивает различные операции сопоставления, что позволяет обнаружить, соответствует ли указанный предикат весь поток. Все соответствующие операции являются окончательными операциями и возвращают значение Boolean.
Кода -копия выглядит следующим образом:
Boolean AnyStartSwitha =
StringCollection
.транслировать()
.anymatch ((s) -> s.startswith ("a"));
System.out.println (AnyStartSwitha);
Boolean allStartSwitha =
StringCollection
.транслировать()
.allmatch ((s) -> s.startswith ("a"));
System.out.println (AllStartSwitha);
Логический non -startswithz =
StringCollection
.транслировать()
.nonematch ((s) -> s.startswith ("z"));
System.out.println (nonestartswithz);
Считать
Подсчет - это окончательная операция, которая возвращает количество элементов в потоке, а тип возвратного значения длинный.
Кода -копия выглядит следующим образом:
Long STARTSWITHB =
StringCollection
.транслировать()
.filter ((s) -> s.startswith ("b")))
.считать();
System.out.println (startSwithb);
Уменьшить правила
Это последняя операция, позволяющая определить несколько элементов в потоке как один элемент через указанную функцию, и результат права представлена дополнительным интерфейсом:
Кода -копия выглядит следующим образом:
Необязательно <string> уменьшен =
StringCollection
.транслировать()
.sorted ()
. Reduce ((S1, S2) -> S1 + "#" + S2);
reved.ifpresent (system.out :: println);
// "AAA1#AAA2#BB1#BB2#BB3#CCC#DDD1#DDD2"
Параллельные потоки
Как упоминалось ранее, поток имеет два типа: последовательные и параллельные.
В следующем примере показано, как повысить производительность через параллельный поток:
Сначала мы создаем большую таблицу без дублирующих элементов:
Кода -копия выглядит следующим образом:
int max = 1000000;
Список <string> values = new ArrayList <> (max);
для (int i = 0; i <max; i ++) {
Uuid uuid = uuid.randomuuid ();
values.add (uuid.tostring ());
}
Затем мы рассчитываем, сколько времени нужно, чтобы сортировать этот поток.
Серийный сортинг:
Кода -копия выглядит следующим образом:
long t0 = System.nanotime ();
long count = values.stream (). Sorted (). Count ();
System.out.println (count);
long t1 = System.nanotime ();
Long Millis = TimeUnit.nanoseconds.tomillis (T1 - T0);
System.out.println (string.format («Последовательный сортирование взято: %d ms», миллисы));
// серийное время: 899 мс
Параллельная сортировка:
Кода -копия выглядит следующим образом:
long t0 = System.nanotime ();
long count = values.parallelStream (). Sorted (). Count ();
System.out.println (count);
long t1 = System.nanotime ();
Long Millis = TimeUnit.nanoseconds.tomillis (T1 - T0);
System.out.println (string.format ("Параллельный сортинг взял: %d ms", millis));
// Параллельная сортировка занимает время: 472 мс
Вышеупомянутые два кода почти одинаковы, но параллельная версия составляет 50%.
Карта
Как упоминалось ранее, тип карты не поддерживает поток, но MAP предоставляет несколько новых и полезных методов для выполнения некоторых ежедневных задач.
Кода -копия выглядит следующим образом:
Map <integer, string> map = new hashmap <> ();
для (int i = 0; i <10; i ++) {
map.putifabsent (i, "val" + i);
}
map.foreach ((id, val) -> System.out.println (val));
Приведенный выше код легко понять.
В следующем примере показаны другие полезные функции на карте:
Кода -копия выглядит следующим образом:
map.computeifpresent (3, (num, val) -> val + num);
map.get (3);
map.computeifpresent (9, (num, val) -> null);
map.containskey (9);
map.computeifabsent (23, num -> "val" + num);
map.containskey (23);
map.computeifabsent (3, num -> "bam");
map.get (3);
Далее, покажите, как удалить элемент на карте, который соответствует всем клавишам:
Кода -копия выглядит следующим образом:
map.remove (3, "val3");
map.get (3);
map.remove (3, "val33");
map.get (3);
Еще один полезный метод:
Кода -копия выглядит следующим образом:
map.getordefault (42, «не найдено»);
Также легко объединить элементы карты:
Кода -копия выглядит следующим образом:
map.merge (9, "val9", (value, newvalue) -> value.concat (newvalue));
map.get (9);
map.merge (9, "concat", (value, newvalue) -> value.concat (newvalue));
map.get (9);
Что делает Merge, так это вставьте, если ключевое имя не существует, в противном случае объединяет значение, соответствующее исходному ключу, и вновь включите его в карту.
9. Дата API
Java 8 содержит совершенно новый набор API времени и дат в рамках пакета Java.Time. Новая дата API аналогична библиотеке Joda-Time с открытым исходным кодом, но не совсем то же самое.
Часы
Класс часов предоставляет метод доступа к текущей дате и времени. Конкретный момент времени также может быть представлен в мгновенном классе, который также может использоваться для создания старых объектов java.util.date.
Кода -копия выглядит следующим образом:
Часы = часы.systemDefaultzone ();
Long Millis = clock.millis ();
Instant instant = clock.instant ();
Date LegacyDate = date.from (Instant);
Часовой пояс
В новом API часовой пояс представлен ZoneID. Часовой пояс можно легко получить с помощью статического метода. Часовой пояс определяет разницу во времени до времени UTS, что чрезвычайно важно при преобразовании между объектом мгновенной точки времени и объектом локальной даты.
Кода -копия выглядит следующим образом:
System.out.println (zeanId.getAvailablezoneids ());
// печатает все доступные идентификаторы часового пояса
ZoneID Zone1 = ZoneId.of («Европа/Берлин»);
ZoneId Zone2 = ZoneId.of («Бразилия/Восток»);
System.out.println (Zone1.getRules ());
System.out.println (Zone2.getRules ());
// Zonerules [CurrentStandardoffset =+01: 00]
// Zonerules [CurrentStandardoffset = -03: 00]
Localtime Local Time
LocalTime определяет время без информационной пояс, например, 10 вечера, или 17:30:15. Следующий пример создает два локального времени, используя часовой пояс, созданный предыдущим кодом. Затем сравнивается время, и разница во времени между двумя разами рассчитывается в часы и минутах:
Кода -копия выглядит следующим образом:
Localtime now1 = localtime.now (Zone1);
Localtime now2 = localtime.now (Zone2);
System.out.println (Now1.isbefore (Now2));
Долгие часы между = chronounit.hours.wedween (now1, now2);
Длинные минуты между = chronounit.minutes.ween (now1, now2);
System.out.println (часы между промежуточными);
System.out.println (минута международна);
LocalTime предоставляет множество заводских методов для упрощения создания объектов, включая строки времени анализа.
Кода -копия выглядит следующим образом:
Localtime поздно = localtime.of (23, 59, 59);
System.out.println (поздно);
DateTimeFormatter Germanformatter =
DateTimeFormatter
.oflocalizeTime (formatstyle.short)
.withlocale (locale.german);
Localtime leettime = localtime.parse ("13:37", Germanformater);
System.out.println (Leettime);
Localdate Local Date
LocalDate представляет собой точную дату, такую как 2014-03-11. Значение этого объекта неизменно и в основном такое же, как и у Localtime. В следующем примере показано, как добавить/лун/год к объекту даты. Также обратите внимание, что эти объекты неизменны, и операция возвращает новый экземпляр.
Кода -копия выглядит следующим образом:
Localdate сегодня = localdate.now ();
LocalDate завтра = сегодня. PLUS (1, Chronounit.Days);
Localdate вчера = завтра. Minusdays (2);
LocalDate IndependenceAly = LocalDate.of (2014, месяц. Июль, 4);
Dayofweek DayOfweek = Independence.GetDayOfweek ();
System.out.println (Dayofweek);
Расположение типа LocalDate из строки так же просто, как и анализ LocalTime:
Кода -копия выглядит следующим образом:
DateTimeFormatter Germanformatter =
DateTimeFormatter
.oflocalizedDate (FormatStyle.Medium)
.withlocale (locale.german);
Localdate xmas = localdate.parse ("24.12.2014", Germanformatter);
System.out.println (Рождество);
LocalDateTime Local DateTime
LocalDateTime представляет как время, так и дату, что эквивалентно слиянию содержимого первых двух раздела в один объект. LocalDateTime, как Localtime и LocalDate, оба неизменны. LocalDateTime предоставляет некоторые методы для доступа к конкретным полям.
Кода -копия выглядит следующим образом:
LocalDateTime sylvester = localdateTime.of (2014, месяц. Декабрь, 31, 23, 59, 59);
DayOfweek DayOfweek = sylvester.getDayOfweek ();
System.out.println (Dayofweek);
Месяц месяца = sylvester.getmonth ();
System.out.println (месяц);
Long MinuteOfDay = sylvester.getLong (chronofield.minute_of_day);
System.out.println (MinuteOfDay);
Просто прикрепите информацию о часовом поясе, и она может быть преобразована в мгновенный объект.
Кода -копия выглядит следующим образом:
Мгновенный мгновенный = Сильвестр
.atzone (ZoneId.SystemDefault ())
.toinstant ();
Date LegacyDate = date.from (Instant);
System.out.println (LegacyDate);
Форматирование LocalDateTime - это то же самое, что форматирование времени и даты.
Кода -копия выглядит следующим образом:
DateTimeFormatter Formatter =
DateTimeFormatter
.ofpattern ("Mmm dd, yyyy - Hh: мм");
LocaldateTime parsed = localdateTime.parse ("3 ноября 2014 - 07:13", форматт);
String string = formatter.format (parsed);
System.out.println (String);
В отличие от java.text.numberformat, новая версия DateTimeFormatter неизменна, поэтому она безопасна.
Подробная информация о формате времени и даты: http://download.java.net/jdk8/docs/api/java/time/format/datetimeformatter.html
10. Примечания к аннотации
Многочисленные аннотации поддерживаются в Java 8. Давайте сначала посмотрим на пример, чтобы понять, что это значит.
Во -первых, определите упакованные подсказки, чтобы разместить определенный набор аннотаций подсказки:
Кода -копия выглядит следующим образом:
@Interface намеки {
Подсказка [] value ();
}
@Repeatable (nts.class)
@Interface намек {
String value ();
}
Java 8 позволяет нам использовать аннотации одного и того же типа несколько раз, просто маркируйте аннотацию @Repeatable.
Пример 1: Используйте класс обертки в качестве контейнера для хранения нескольких аннотаций (старый метод)
Кода -копия выглядит следующим образом:
@Hints ({ @hint ("hint1"), @hint ("hint2")})
класс человек {}
Пример 2: Использование нескольких аннотаций (новый метод)
Кода -копия выглядит следующим образом:
@Hint ("kint1")
@Hint ("hint2")
класс человек {}
Во втором примере компилятор Java косвенно определит для вас аннотацию @hints.
Кода -копия выглядит следующим образом:
HINT HINT = person.class.getAnnotation (Hint.class);
System.out.println (подсказка);
Ntsts nts1 = person.class.getannotation (nts.class);
System.out.println (Hints1.value (). Length);
Nticle [] ntsts2 = person.class.getannotationsbytype (nt.class);
System.out.println (Hints2.length);
Даже если мы не определяем аннотацию @hints на классе человека, мы все равно можем получить аннотацию @hints через Getannotation (nts.class).
Кроме того, аннотации Java 8 были добавлены к двум новым целям:
Кода -копия выглядит следующим образом:
@Target ({elementtype.type_parameter, elementtype.type_use})
@Interface myannotation {}
Это все о новых функциях Java 8, и, безусловно, есть больше функций, ожидающих обнаружения. В JDK 1.8 есть много полезных вещей, таких как Arrays.parallelsort, Stampedlock и завершаемая номера.