JavaRush /Java блог /Random UA /Кава-брейк #37. Нове майбутнє для Java. JVM, Kotlin та пе...

Кава-брейк #37. Нове майбутнє для Java. JVM, Kotlin та перспективи Java після 2020 року

Стаття з групи Random UA
Джерело: Medium В основному Java критикують за дві речі: багатослівність і кількість шаблонного коду, що генерується в багатьох випадках без потреби. Хоча мені завжди подобалася Java, я не можу сказати, що ці твердження є помилковими. Це справді правда: зайва деталізація в Java може іноді сильно дратувати. Однак ми маємо визнати, що живемо не в ідеальному світі, і в більшості випадків нам доводиться вибирати найменше з двох лих. Кава-брейк #37.  Нове майбутнє для Java.  JVM, Kotlin та перспективи Java після 2020 року - 1З часів створення Java не була ідеальною: ми все це знаємо, але головне питання полягає в тому, чому раніше нічого не робабо для вирішення цих проблем. Мені здається, єдина причина, через яку зміни зайняли так багато часу, полягає в тому, що мови Java не вистачало конкуренції, і все було так, як було. Java домінувала на ринку, ймовірно, через відсутність серйозних конкурентів і великих зусиль, зроблених спочатку Sun, а потім Oracle. Високий рівень безпеки типів, що забезпечується Java, і хороша структуризація мови зробабо його дуже популярним для великих проектів. Крім того, це мультиплатформна мова, яка працює на власній віртуальній машині. Якщо ще зважити на автоматичну оптимізацію продуктивності за допомогою JIT-компілятора, все це зводить до мінімуму вплив погано написаного коду. Загалом виходить досить вагомий набір причин для використання Java. Але що сталося потім? Сталося те, що на ринку з'явабося нові мови, здатні працювати в тій же JVM, що й Java. Мови, які усували деякі з найбільших незручностей у Java і в ряді випадків пропонували розробникам більш приємне середовище меншим порогом входження. Перш ніж ми продовжимо, давайте підіб'ємо підсумки і зробимо короткий екскурс в історію мов JVM.

Історія мов JVM

Для початку я хотів би прояснити одну річ: я не згадав деякі з існуючих мов JVM тому, що вони ніколи не мали достатньої підтримки, щоб вважатися кандидатами для широкого використання в нашій галузі. А тепер розпочнемо наш короткий огляд історії мов JVM. Першим у нас буде мова Java - найстаріша і найпопулярніша мова у світі JVM. Офіційно Java випущена в січні 1996 року, тому мова існує вже 24 роки. Непогано, правда? Спочатку Java була суто імперативною мовою, яка слідувала об'єктно-орієнтованому стилю програмування; це також була строго типізована мова. Синтаксис Java певною мірою схожий на мови C++ і C, але він вважається покращеною версією, оскільки Java писати код набагато простіше, ніж C або C++. З іншого боку, ми маємо найбільший аргумент серед його недоброзичливців — багатослівність (verbosity). Другою мовою JVM був Groovy. Він існує з 2003 року, хоча його перша стандартизована версія 1.0 з'явилася лише у січні 2007 року. Перевага Groovy в тому, що його можна використовувати як мову сценаріїв. Groovy – це мова з динамічною типізацією, тому перевірка типів виконується під час виконання. Це одна з причин, чому деяким розробникам не подобається Groovy. Ви пишете свій код на Groovy і під час компіляції він виглядає правильно, але потім під час виконання ви розумієте, що щось не так. Потім з'явилася ще одна популярна мова: ми говоримо про Scala. Його випустабо 2004 року. Він приніс у світ JVM нову модель роботи: функціональне програмування та декларативний підхід. Загалом Scala була першою мовою, де з'явилася концепція незмінності, яка потім використовувалася в удосконаленні Java. З іншого боку, недоброзичливцям Scala не подобається через складну граматику і досить низьку читальність. Наступною мовою, яка з'явилася у світі JVM, була Clojure, чисто функціональна мова. Він став досить популярним останнім часом, хоч з'явився ще 2007 року. Clojure – це мова на основі LISP, що відрізняється простотою та використанням простих функцій. Серед його недоліків можна згадати те, що він динамічно типізований (як і Groovy), а крива навчання набагато крутіше, оскільки його синтаксис повністю відрізняється від інших мов JVM. І, нарешті, у нас є Kotlin. Kotlin вперше з'явився у лютому 2016 року, і з того часу його популярність не переставала зростати. Він розроблений компанією JetBrains з головною метою: усунути найвідоміші проблеми Java. За своїм задумом Kotlin зберіг всі переваги Java, але в той же час вирішив багато проблем. Це найважливіші мови JVM. Як я вже сказав, ми пропустабо деякі інші мови, які не такі популярні: Jython, JRuby, Ceylon, Fantom і т.д. За бажанням, ви можете дізнатися весь список існуючих мов JVMу Вікіпедії. Напевно, ви вже зрозуміли, що Java не мала особливих конкурентів у перші вісім чи десять років після створення, але з того часу все змінилося. Отже, конкуренція – це добре чи погано?

Переваги зростаючої конкуренції

Java не сильно змінилася за перші роки свого існування. Мабуть, тому, що цього не було потреби. Ця мова широко використовувалася і завжди була дуже популярна, незважаючи на те, що вона далека від досконалості. Але потім з'явабося конкуренти, сучасніші мови, які пропонували нові функції та вирішували деякі проблеми, від яких Java-розробники страждали довгий час. Наприклад, давайте подивимося на мову Scala. Популярність Scala зростала з 2009 року. Розробники вітали цей новий функціональний стиль, який дав їм більшу гнучкість, а також можливість безпечно та легко писати паралельний код. Як Oracle відреагувала на цю нову тенденцію? У 2014 році з'явабося Java Lambdas та Streams. Я думаю, ми можемо погодитися, що саме тоді Java зробила свій найбільший крок до перемоги над Scala. Наразі будь-який програміст знає, що Scala вже не в тренді. Кава-брейк #37.  Нове майбутнє для Java.  JVM, Kotlin та перспективи Java після 2020 року - 2Ще одна перевага більшої кількості конкурентів у світі JVM — постійні покращення, які вносять у компілятор JIT та JVM. Тепер набагато більше людей зацікавлені в оптимізації JVM та покращенні продуктивності. Тож конкуренція – це добре для всіх! Найсвіжішою альтернативою Java стала мова Kotlin. Його поява стала дуже важливою для розвитку Java, оскільки нова мова в певному сенсі вказала Oracle шлях уперед. Приклад Kotlin показав, що можна зберегти плюси Java, але створити більш компактну мову, якою швидше писати код. Якщо подивитися на графік Google Trends, можна побачити, що з 2016 до 2018 року популярність Kotlin стрімко зростала. Але за останні два роки ажіотаж упав. Кава-брейк #37.  Нове майбутнє для Java.  JVM, Kotlin та перспективи Java після 2020 року - 3Компанія Oracle уважно вивчила реакцію галузі на Kotlin. Якщо ви побачите примітки до випуску JDK 15 , ви побачите, що деякі з нових функцій Java є копіями того, що з'явилося в Kotlin. Це нові записи Java , нові текстові блоки (багаторядкові рядки з потрійними лапками) і новий оператор switch, який є копією оператора whenв Kotlin. Все, про що ми говорабо, я називаю "котлінізацією Java". Ставши сильнішим конкурентом, Kotlin вказав Java шлях, яким потрібно йти.

«Котлінізація» Java

Деякі з майбутніх функцій Java стануть значним покращенням з погляду читабельності та усунення однієї з найслабших сторін мови Java – її багатослівності. Можна стверджувати, що багато анонсованих функцій Java підозріло схожі на деякі функції Kotlin. Але врахуйте, що більшість із них є попередніми версіями . Це означає, що якщо ви встановите JDK 14 або JDK 15 (коли його випустять), ви не зможете використовувати їх за промовчанням. Прев'ю функцій Java - це нові функції, які представлені в релізі, але за замовчуванням відключені. Вони включені в нову версію тільки для того, щоб зібрати відгуки спільноти розробників, тому вони ще можуть бути змінені. Ось чому не рекомендується використовувати їх у робочому коді. Щоб увімкнути їх під час компіляції, вам потрібно буде зробити таке:
javac --enable-preview --release 14
Якщо ви хочете увімкнути їх під час виконання, вам потрібно буде запустити наступне:
java --enable-preview YourClass
Звичайно, ви також можете включити їх у своєму середовищі IDE, але будьте обережні, щоб не включати попередній перегляд за промовчанням у всіх ваших нових проектах! Давайте подивимося на ті зміни, які вплинуть на наш кодинг у майбутніх версіях Java.

Записи Java

Записи Java - це функція, яку багато хто з нас давно вимагає. Я припускаю, що ви опинабося в ситуації ситуації, коли вам потрібно було реалізувати toString , hashCode , equals , а також гетери для кожного існуючого поля. У Kotlin для вирішення цієї проблеми є класи даних , і Java має намір зробити те ж саме, випускаючи класи записів , які вже є у Scala у вигляді case classes . Основна мета цих класів - зберігати незмінні дані в об'єкті. Давайте на прикладі подивимося, як краще може стати Java. Ось скільки коду нам довелося б написати, щоб створювати та порівнювати наш клас Employee:
package com.theboreddev.java14;

import java.util.Objects;

public class Employee {
    private final String firstName;
    private final String surname;
    private final int age;
    private final Address address;
    private final double salary;

    public Employee(String firstName, String surname, int age, Address address, double salary) {
        this.firstName = firstName;
        this.surname = surname;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSurname() {
        return surname;
    }

    public int getAge() {
        return age;
    }

    public Address getAddress() {
        return address;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Double.compare(employee.salary, salary) == 0 &&
                Objects.equals(firstName, employee.firstName) &&
                Objects.equals(surname, employee.surname) &&
                Objects.equals(address, employee.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, surname, age, address, salary);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "firstName='" + firstName + '\'' +
                ", surname='" + surname + '\'' +
                ", age=" + age +
                ", address=" + address +
                ", salary=" + salary +
                '}';
    }
}
А також об'єкт Address, який він містить:
import java.util.Objects;

public class Address {
    private final String firstLine;
    private final String secondLine;
    private final String postCode;

    public Address(String firstLine, String secondLine, String postCode) {
        this.firstLine = firstLine;
        this.secondLine = secondLine;
        this.postCode = postCode;
    }

    public String getFirstLine() {
        return firstLine;
    }

    public String getSecondLine() {
        return secondLine;
    }

    public String getPostCode() {
        return postCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(firstLine, address.firstLine) &&
                Objects.equals(secondLine, address.secondLine) &&
                Objects.equals(postCode, address.postCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstLine, secondLine, postCode);
    }

    @Override
    public String toString() {
        return "Address{" +
                "firstLine='" + firstLine + '\'' +
                ", secondLine='" + secondLine + '\'' +
                ", postCode='" + postCode + '\'' +
                '}';
    }
}
Напевно, тут занадто багато коду для чогось такого простого, чи не так? Давайте тепер подивимося, як це буде виглядати з новими записами Java:
public record EmployeeRecord(String firstName, String surname, int age, AddressRecord address, double salary) {
}
І ще не забудемо клас Address:
public record AddressRecord(String firstLine, String secondLine, String postCode) {
}
Це те саме, що ми написали раніше з такою великою кількістю коду. Погодьтеся: це чудово. І кількість коду, яку ми збираємось заощадити, і простота написання! Давайте подивимося, в чому відмінності нового оператора switch.

Покращений операторswitch

Новий оператор switchJava вирішить частину старих проблем, включаючи деякі помилки і дублювання коду. З новим оператором switchцю проблему буде вирішено. Щоб пояснити це на прикладі, ми збираємося створити перелік DayOfTheWeekJava:
public enum DayOfTheWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}
Після цього наш switchповідомить, яка позиція на тижні відповідає цьому дню. Давайте спочатку побачимо, як ми можемо зробити це в даний час, використовуючи Java 11.
final DayOfTheWeek dayOfTheWeek = DayOfTheWeek.THURSDAY;

        int position = 0;

        switch (dayOfTheWeek) {
            case MONDAY:
                position = 1;
                break;
            case TUESDAY:
                position = 2;
                break;
            case WEDNESDAY:
                position = 3;
                break;
            case THURSDAY:
                position = 4;
                break;
            case FRIDAY:
                position = 5;
                break;
            case SATURDAY:
                position = 6;
                break;
            case SUNDAY:
                position = 7;
                break;
        }

        System.out.println("Day " + dayOfTheWeek + " is in position " + position + " of the week");
З поточним оператором switchнам потрібно буде використовувати змінну, і якщо ми пропустимо один із днів тижня, наш код відмінно компілюватиметься. Це одна з проблем операторів switch: вони занадто схильні до помилок. То як же Java 14 покращує ситуацію? Давайте подивимося:
final DayOfTheWeek dayOfTheWeek = DayOfTheWeek.THURSDAY;

        int position = switch (dayOfTheWeek) {
            case MONDAY -> 1;
            case TUESDAY -> 2;
            case WEDNESDAY -> 3;
            case THURSDAY -> 4;
            case FRIDAY -> 5;
            case SATURDAY -> 6;
            case SUNDAY -> 7;
        };

        System.out.println("Day " + dayOfTheWeek + " is in position " + position + " of the week");
Як бачите, нові оператори switchможуть використовуватися як вираз, а не лише оператор. Результат більш лаконічний та виразний. Цього було б достатньо, щоб переконати багатьох із нас використовувати їх, але одне з основних покращень полягає в тому, що тепер оператори switchне компілюватимуться, якщо ми не охопимо всі випадки в нашому switch. Він покаже нам ось таку помилку, наприклад:
Error:(9, 24) java: the switch expression does not cover all possible input values
Відтепер у наших операторах switchпропустити регістр буде неможливо. Це дуже схоже на оператори whenKotlin, про які ви можете прочитати в документації . Погляньмо також на нові текстові блоки.

Текстові блоки

Ви коли-небудь скаржабося на те, як важко привласнити великий двійковий об'єкт JSON змінної Java? У Java є багаторядкові послідовності, які ви можете описати, уклавши їх у потрійні лапки. Коли ця функція буде офіційно випущена, стане набагато простіше описувати довгі послідовності кілька рядків. Давайте подивимося на різницю між двома режимами. Якщо ми хочемо використати відформатований JSON у змінній, виходить погано:
final String text = "{\"widget\": {\n" +
                "    \"debug\": \"on\",\n" +
                "    \"window\": {\n" +
                "        \"title\": \"Sample Konfabulator Widget\",\n" +
                "        \"name\": \"main_window\",\n" +
                "        \"width\": 500,\n" +
                "        \"height\": 500\n" +
                "    },\n" +
                "    \"image\": { \n" +
                "        \"src\": \"Images/Sun.png\",\n" +
                "        \"name\": \"sun1\",\n" +
                "        \"hOffset\": 250,\n" +
                "        \"vOffset\": 250,\n" +
                "        \"alignment\": \"center\"\n" +
                "    },\n" +
                "    \"text\": {\n" +
                "        \"data\": \"Click Here\",\n" +
                "        \"size\": 36,\n" +
                "        \"style\": \"bold\",\n" +
                "        \"name\": \"text1\",\n" +
                "        \"hOffset\": 250,\n" +
                "        \"vOffset\": 100,\n" +
                "        \"alignment\": \"center\",\n" +
                "        \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n" +
                "    }\n" +
                "}} ";
З іншого боку, коли випустять нові текстові блоки, все стане набагато простіше:
final String multiLineText = """
                {"widget": {
                    "debug": "on",
                    "window": {
                        "title": "Sample Konfabulator Widget",
                        "name": "main_window",
                        "width": 500,
                        "height": 500
                    },
                    "image": {\s
                        "src": "Images/Sun.png",
                        "name": "sun1",
                        "hOffset": 250,
                        "vOffset": 250,
                        "alignment": "center"
                    },
                    "text": {
                        "data": "Click Here",
                        "size": 36,
                        "style": "bold",
                        "name": "text1",
                        "hOffset": 250,
                        "vOffset": 100,
                        "alignment": "center",
                        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
                    }
                }}
                """;
Це явно виглядає краще. Все це вже підтримується Kotlin, як ви можете побачити в визначеннях його типів . Отже, ми побачабо, що Java «успадковує» багато вирішення власних проблем від одного зі своїх конкурентів: Kotlin. Ми не знаємо, чи вчасно відреагував Oracle на боротьбу зі зростанням популярності Kotlin чи, можливо, це сталося надто пізно. Особисто я вважаю, що Java робить правильні кроки вперед, навіть якщо ці зміни були ініційовані її конкурентами і можуть статися з деяким запізненням.

Висновок

Я вважаю, що конкуренція — найкраще, що колись траплялося з мовою Java. У мене склалося враження, що інакше Java спочивала б на лаврах. Крім того, конкуренти Java показали, що можливий інший спосіб програмування, що показує, як рухатися вперед і уникати застарілих та неефективних способів написання коду. Майбутні зміни зроблять мову Java більш потужною, ніж будь-коли, мовою, адаптованою до сучасної доби, мовою, яка хоче розвиватися.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ