Джерело: DZone У цій статті перераховуються десять функцій програмування мовою Java, які часто використовуються розробниками в їх повсякденній роботі.
1. Фабричний метод у колекціях (Collection Factory Method)
Колекції — одна з функцій, що найчастіше використовуються в програмуванні. Вони використовуються як контейнер, в якому ми зберігаємо об'єкти та передаємо їх далі. Колекції також використовуються для сортування, пошуку та повторення об'єктів, що помітно спрощує життя програміста. Вони мають кілька основних інтерфейсів, таких як List, Set, Map, а також кілька реалізацій. Традиційний спосіб створення Collections та Maps може здатися багатьом розробникам багатослівним. Тому Java 9 з'явилося кілька лаконічних фабричних методів. List :
List countries =List.of("Bangladesh","Canada","United States","Tuvalu");
Set :
Set countries =Set.of("Bangladesh","Canada","United States","Tuvalu");
Фабричний метод дуже зручний, коли ми хочемо створити незмінні контейнери. Але якщо ви збираєтеся створювати змінні колекції, рекомендується використовувати традиційний підхід.
2. Висновок локального типу (Local Type Inference)
Java 10 додали висновок типів для локальних змінних. До цього розробникам доводиться двічі вказувати типи при оголошенні та ініціалізації об'єкта. Це було дуже втомливо. Подивіться наступний приклад:
Map> properties =newHashMap<>();
Тут вказано тип інформації з обох сторін. Якщо ж ми визначимо його в одному місці, то читач коду та компілятор Java легко зрозуміють, що це має бути тип Map. Висновок локального типу робить саме це. Ось приклад:
var properties =newHashMap>();
Тепер все пишеться лише один раз і код виглядає не набагато гіршим. А коли ми викликаємо метод і зберігаємо результат у змінній, то код стає ще коротшим. Приклад:
var properties =getProperties();
І далі:
var countries =Set.of("Bangladesh","Canada","United States","Tuvalu");
Хоча висновок локального типу здається зручною функцією, дехто її критикує. Деякі розробники стверджують, що таке знижується читабельність. А це важливіше, ніж стислість.
3. Розширені вирази Switch
Традиційний оператор switch існував у Java від початку і тоді він нагадував C і C++. Це було нормально, але в міру розвитку мови цей оператор не пропонував нам жодних покращень, аж до Java 14. Звичайно, у нього були деякі недоліки. Найсумніше відомим було провалювання (fall-through): Щоб вирішити цю проблему, розробники використовували оператори break, які значною мірою є шаблонним кодом. Однак у Java 14 з'явилася вдосконалена версія оператора switch з набагато більшим переліком функцій. Тепер нам більше не потрібно додавати оператори break і це вирішує проблему провалу. Крім того, оператор switch може повертати значення, що означає, що ми можемо використовувати його як вираз і надавати його змінній.
int day =5;String result =switch(day){case1,2,3,4,5->"Weekday";case6,7->"Weekend";default->"Unexpected value: "+ day;};
4. Записи
Хоча Записи (Records) - це відносно нова функція, що з'явилася в Java 16, багато розробників знаходять її дуже корисною, головним чином завдяки створенню незмінних об'єктів. Часто нам потрібні об'єкти даних у нашій програмі для зберігання або передачі значень з одного методу до іншого. Наприклад, клас для перенесення координат x, y та z, який ми запишемо наступним чином:
packageca.bazlur.playground;importjava.util.Objects;publicfinalclassPoint{privatefinalint x;privatefinalint y;privatefinalint z;publicPoint(int x,int y,int z){this.x = x;this.y = y;this.z = z;}publicintx(){return x;}publicinty(){return y;}publicintz(){return z;}@Overridepublicbooleanequals(Object obj){if(obj ==this)returntrue;if(obj ==null|| obj.getClass()!=this.getClass())returnfalse;var that =(Point) obj;returnthis.x == that.x &&this.y == that.y &&this.z == that.z;}@OverridepublicinthashCode(){returnObjects.hash(x, y, z);}@OverridepublicStringtoString(){return"Point["+"x="+ x +", "+"y="+ y +", "+"z="+ z +']';}}
Клас здається надто багатослівним. За допомогою записів весь цей код можна замінити на більш короткий варіант:
Метод – це контракт, у якому ми позначаємо умови. Ми вказуємо параметри з їх типом, а також тип, що повертається. Потім ми очікуємо, що при виклику методу він поводитиметься відповідно до контракту. Однак, часто ми отримуємо null з методу замість значення зазначеного типу. Це помилка. Щоб її усунути, ініціатор зазвичай перевіряє значення з умовою if, незалежно від того, чи це значення є нульовим чи ні. Приклад:
publicclassPlayground{publicstaticvoidmain(String[] args){String name =findName();if(name !=null){System.out.println("Length of the name : "+ name.length());}}publicstaticStringfindName(){returnnull;}}
Подивіться на наведений вище код. Передбачається, що метод findName поверне значення String але він повертає null. Ініціатор тепер повинен спочатку перевірити nulls, щоб упоратися з проблемою. Якщо ініціатор забуває зробити це, то зрештою ми отримаємо NullPointerException . З іншого боку, якби сигнатура методу вказувала на можливість неповернення значення, це вирішило б всю плутанину. І ось тут нам може допомогти Optional .
importjava.util.Optional;publicclassPlayground{publicstaticvoidmain(String[] args){Optional optionalName =findName();
optionalName.ifPresent(name ->{System.out.println("Length of the name : "+ name.length());});}publicstaticOptionalfindName(){returnOptional.empty();}}
Тут ми переписали метод findName з Optional , у якому вказано можливість повертати ніякого значення. Це заздалегідь попереджає програмістів та усуває проблему.
6. Java Date Time API
Кожен розробник тією чи іншою мірою плутається з обчисленням дати та часу. Це не перебільшення. В основному це було пов'язано з відсутністю хорошого Java API для роботи з датами та часом. Зараз ця проблема вже не актуальна, тому що в Java 8 з'явився відмінний набір API у пакеті java.time, який вирішує всі питання, пов'язані з датою та часом. Пакет java.time має безліч інтерфейсів та класів, які усувають більшість проблем, включаючи часові пояси. Найчастіше у цьому пакеті використовуються такі класи:
Приклад використання класу LocalTime для розрахунку часу:
LocalTime time =LocalTime.of(20,30);int hour = time.getHour();int minute = time.getMinute();
time = time.withSecond(6);
time = time.plusMinutes(3);
Додавання часового поясу:
ZoneId zone =ZoneId.of("Canada/Eastern");LocalDate localDate =LocalDate.of(2022,Month.APRIL,4);ZonedDateTime zonedDateTime = date.atStartOfDay(zone);
7.NullPointerException
Кожен розробник ненавидить виняток NullPointerException. Особливо складно буває, коли StackTrace не надає корисної інформації, в чому полягає проблема. Щоб це продемонструвати, погляньмо на приклад коду:
packagecom.bazlur;publicclassMain{publicstaticvoidmain(String[] args){User user =null;getLengthOfUsersName(user);}publicstaticvoidgetLengthOfUsersName(User user){System.out.println("Length of first name: "+ user.getName().getFirstName());}}classUser{privateName name;privateString email;publicUser(Name name,String email){this.name = name;this.email = email;}//getter//setter}className{privateString firstName;privateString lastName;publicName(String firstName,String lastName){this.firstName = firstName;this.lastName = lastName;}//getter//setter}
Подивіться на основний метод у цьому уривку. Ми бачимо, що з'явиться далі NullPointerException . Якщо ми запустимо та скомпілюємо код у версії до Java 14, то отримаємо наступний StackTrace:
Exception in thread "main"java.lang.NullPointerException
at com.bazlur.Main.getLengthOfUsersName(Main.java:11)
at com.bazlur.Main.main(Main.java:7)
Тут дуже мало інформації, де і чому виник виняток NullPointerException . А ось у Java 14 і пізніших версіях ми отримуємо набагато більше відомостей у StackTrace, що дуже зручно. У Java 14 ми побачимо:
Exception in thread "main"java.lang.NullPointerException:Cannot invoke "ca.bazlur.playground.User.getName()" because "user" is null
at ca.bazlur.playground.Main.getLengthOfUsersName(Main.java:12)
at ca.bazlur.playground.Main.main(Main.java:8)
8. CompletableFuture
Ми пишемо програми рядково, і зазвичай вони виконуються рядково. Але трапляються випадки, коли нам потрібне паралельне виконання, щоб зробити програму швидше. Для цього ми зазвичай використовуємо Java Thread. Програмування потоків Java не завжди пов'язане з паралельним програмуванням. Натомість воно дає нам можливість скласти кілька незалежних модулів програми, які виконуватимуться незалежно і часто навіть асинхронно. Однак програмування потоків є досить складним, особливо для новачків. Ось чому Java 8 пропонує простіший API, який дозволяє виконувати частину програми асинхронно. Давайте подивимося приклад. Припустимо, потрібно викликати три REST API, а потім об'єднати результати. Ми можемо викликати їх поодинці. Якщо кожен із них займає близько 200 мілісекунд, то загальний час їх отримання займе 600 мілісекунд. А що, якби ми могли запускати їх паралельно? Оскільки сучасні процесори зазвичай багатоядерні, вони можуть легко обробляти три виклики rest на трьох різних процесорах. Використовуючи CompletableFuture, ми можемо це зробити.
Лямбда-вирази, мабуть, – найпотужніша функція Java. Вони змінабо, як ми пишемо код. Лямбда-вираз схоже на анонімну функцію, яка може приймати аргументи та повертати значення. Ми можемо надати функцію змінної і передати її методу як аргументи, а метод може її повернути. Має тіло. Єдина відмінність від методу у тому, що тут немає імені. Вирази короткі та лаконічні. Зазвичай вони містять великої кількості шаблонного коду. Давайте подивимося приклад, у якому нам потрібно перерахувати всі файли в каталозі з розширенням java.
var directory =newFile("./src/main/java/ca/bazlur/playground");String[] list = directory.list(newFilenameFilter(){@Overridepublicbooleanaccept(File dir,String name){return name.endsWith(".java");}});
Якщо уважно подивитися на цей уривок коду, то ми передали метод анонімний внутрішній клас list() . А у внутрішній клас ми розмістабо логіку для фільтрації файлів. По суті нас цікавить саме ця частина логіки, а не шаблон навколо логіки. Лямбда-вираз дозволяє нам видалити весь шаблон і ми можемо написати код, який нас цікавить. Ось приклад:
var directory =newFile("./src/main/java/ca/bazlur/playground");String[] list = directory.list((dir, name)-> name.endsWith(".java"));
Звичайно, це лише один приклад, у лямбда-вираження є багато інших переваг.
10. Stream API
У нашій повсякденній роботі одним із найпоширеніших завдань є обробка набору даних. У ній існує кілька загальних операцій, таких як фільтрація, перетворення та збирання результатів. До Java 8 такі операції були імперативними за своєю суттю. Ми повинні були написати код для нашого наміру (тобто чого ми хотіли досягти) і як би ми цього хотіли б зробити. З винаходом лямбда-виразу та Stream API тепер ми можемо записувати функції обробки даних декларативно. Ми лише вказуємо свій намір, і нам не потрібно записувати, як ми отримуємо результат. Ось приклад: У нас є список книг, і ми хочемо знайти всі імена книг по Java, розділені комами та відсортовані.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ