company_banner

Размышления о Java 8 и Java 11 в ожидании Java 17

Автор оригинала: Otavio Santana
  • Перевод
Этот материал посвящён некоторым особенностям Java 8 и Java 11. Его можно рассматривать как отправную точку для подготовки к освоению очередного LTS-релиза платформы — Java 17.



В мире Java есть одна приятная особенность, которая связана с жизненным циклом версий платформы. А именно, новый релиз Java выходит каждые 6 месяцев, а каждые 3 года появляется новый LTS-релиз — версия с долгосрочной поддержкой. В настоящий момент LTS-версия платформы представлена Java 11. Поэтому многие компании переходят на неё. Это — заметное движение, так как среди его последствий можно отметить тот факт, что, с выходом в сентябре 2021 года Java 17, новые фреймворки не будут поддерживать Java 8, а в качестве минимальной версии платформы будут рассматривать Java 11.

Цель этой статьи заключается в том, чтобы рассмотреть некоторые общие базовые API Java 8 и Java 11.

В Java 8, в пакете java.util.function, появились новые интерфейсы, актуальные и в Java 11. Мы рассмотрим четыре таких интерфейса:

  • Function
  • Predicate
  • Supplier
  • Consumer

Интерфейс Function представляет функцию, которая принимает один аргумент и выдаёт некий результат:

import java.util.function.Function;

public class FunctionApp {

    public static void main(String[] args) {
        Function<String, Integer> toNumber = Integer::parseInt;
        System.out.println("To number: " + toNumber.apply("234"));
        Function<String, String> upperCase = String::toUpperCase;
        Function<String, String> trim = String::trim;
        Function<String, String> searchEngine = upperCase.andThen(trim);
        System.out.println("Search result: " + searchEngine.apply("   test one two   "));

    }
}

Интерфейс Predicate представляет предикат (логическую функцию) от одного аргумента:

import java.util.function.Predicate;

public class PredicateApp {

    public static void main(String[] args) {
        Predicate<String> startWithA = s -> s.startsWith("A");
        Predicate<String> startWithB = s -> s.startsWith("B");
        System.out.println(startWithA.and(startWithB).test("Animal"));
    }
}

Интерфейс Supplier представляет функцию, не принимающую никаких аргументов, но возвращающую некое значение:

import java.util.Optional;
import java.util.function.Supplier;

public class SupplierApp {

    public static void main(String[] args) {
        Supplier<String> cache = () -> "From Database";
        Optional<String> query = Optional.empty();
        System.out.println(query.orElseGet(cache));
    }
}

Интерфейс Consumer представляет функцию, которая принимает единственное входное значение, но ничего не возвращает:

import java.util.function.Consumer;

public class ConsumerApp {

    public static void main(String[] args) {
        Consumer<String> log = s -> System.out.println("The log " +s);
        Consumer<String> logB = s -> System.out.println("The logB " +s);
        log.andThen(logB).accept("The value A");
    }
}

Если продолжить разговор о функциональных интерфейсах, то можно сказать, что с ними связаны некоторые усовершенствования Java, например — улучшения в реализациях коллекций:

public class ListApp {

    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>(List.of("Bananas", "Melon", "Watermelon"));
        fruits.forEach(System.out::println);
        fruits.removeIf("Bananas"::equals);
        fruits.sort(Comparator.naturalOrder());
        System.out.println("After sort: ");
        fruits.forEach(System.out::println);
    }
}

public class SetApp {

    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>(List.of("Bananas", "Melon", "Watermelon"));
        fruits.forEach(System.out::println);
        fruits.removeIf("Bananas"::equals);
        System.out.println("After sort: ");
        fruits.forEach(System.out::println);
    }
}

public class MapApp {

    public static void main(String[] args) {
        Map<String, String> medias = new HashMap<>();
        medias.put("facebook", "otaviojava");
        medias.put("twitter", "otaviojava");
        medias.put("linkedin", "otaviojava");
        System.out.println("The medias values " + medias);
        medias.forEach((k, v) -> System.out.println("the key: " + k + " the value " + v));
        medias.compute("twitter", (k, v) -> k + '-' + v);
        System.out.println("The medias values " + medias);
        medias.computeIfAbsent("social", k -> "no media found: " + k);
        medias.computeIfPresent("social", (k, v) -> k + " " + v);
        System.out.println("The medias values " + medias);
        medias.replaceAll((k, v) -> v.toUpperCase(Locale.ENGLISH));
        System.out.println("The medias values " + medias);
    }
}

Существуют, кроме того, новые фабричные методы, упрощающие создание интерфейсов коллекций:

import java.util.List;
import java.util.Map;
import java.util.Set;

public class MethodFactory {

    public static void main(String[] args) {
        List<String> fruits = List.of("banana", "apples");
        Set<String> animals = Set.of("Lion", "Monkey");
        Map<String, String> contacts = Map.of("email", "me@gmail.com", "twitter", "otaviojava");
    }
}

Потоки — это последовательности элементов, поддерживающие последовательные и параллельные операции. Потоки в Java можно сравнить с движением воды в водопаде или в реке. Соответствующий API представляет собой надёжный и понятный механизм для работы с коллекциями:

public class StreamApp {

    public static void main(String[] args) {
        List<String> fruits = List.of("Banana", "Melon", "Watermelon");
        fruits.stream().sorted().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
        fruits.stream().sorted().collect(Collectors.toUnmodifiableList());
        Map<Boolean, List<String>> startWithB = fruits.stream().collect(Collectors.partitioningBy(f -> f.startsWith("B")));
        System.out.println("Start with B " + startWithB);
        Map<String, List<String>> initials = fruits.stream().collect(Collectors.groupingBy(s -> s.substring(0)));
        System.out.println("Initials: " + initials);
    }
}

Вот ещё пример:

public class StreamReduceApp {

    public static void main(String[] args) {
        List<BigDecimal> values = List.of(BigDecimal.ONE, BigDecimal.TEN);
        Optional<BigDecimal> total = values.stream().reduce(BigDecimal::add);
        System.out.println(total);
    }
}

Когда вышла платформа Java 8 — был представлен и новый API для работы с датой и временем (пакет java.time), появились новые типы, иммутабельные классы, методы, и, наконец, перечисления. Использование этих перечислений повышает удобство работы с днями недели и месяцами в сравнении с применением их числовых представлений.

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Year;
import java.time.YearMonth;
import java.util.Arrays;

public class DateApp {

    public static void main(String[] args) {
        System.out.println("LocalDateTime: " + LocalDateTime.now());
        System.out.println("Localdate: " + LocalDate.now());
        System.out.println("LocalDateTime: " + LocalDateTime.now());
        System.out.println("YearMonth: " + YearMonth.now());
        System.out.println("Year: " + Year.now());
        System.out.println("Days of weeks: " + Arrays.toString(DayOfWeek.values()));
        System.out.println("Months: " + Arrays.toString(Month.values()));
    }
}

В текущем LTS-релизе платформы, в Java 11, имеются некоторые особенности, упрощающие жизнь разработчиков. Надо отметить, что тут мы лишь очень кратко рассмотрели некоторые особенности Java 8 и Java 11. А если говорить о возможностях, появлявшихся в различных версиях Java, то можно отметить, что существует множество материалов, раскрывающих эти особенности. Например — вот материал о новых возможностях Java 11.

Как вы готовитесь к выходу Java 17?

RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR10

Комментарии 19

    +6

    Потоки в Java можно сравнить с движением воды в водопаде или в реке. 

    Сравнение так сравнение) что это значит вообще?

      +16

      Самурай без меча подобен самураю с мечом только без меча. :)

        +19

        Это означает неудачный выбор статьи для перевода, а план по пиару хостинга надо выполнять!

          +3

          плохо подобранная метафора подобна котенку с дверцей (с)

          +1

          Объясните, зачем эти костыли с интерфейсами на каждый чих? Правда, что не осилили нормальные функциональные типы(например, (String, String) -> String вместо BiFunction) и кусок синтаксиса для их поддержки?

            +1

            Я думаю для обратной совместимости.
            А в чем отличие если люди все равно пишут (x, y) -> x

              0

              Обратная совместимость это, конечно, магические слова, которыми всё можно оправдать, но я не очень понимаю, каким она тут боком.

              Разница в том, что с нормальным синтаксисом не надо было бы придумывать 100500 разных интерфейсов про одно и то же. Плюс не было бы тайных знаний про то, что вот, есть, дескать, особые интерфейсы, которые можно представить лямбдами. И не надо было бы компилятору про это рассказывать костыльной аннотацией.

                +3
                Аннотация
                @FunctionalInterface
                нужна лишь для проверки на этапе компиляции, что интерфейс действительно функциональный (т.е. содержит ровно один абстрактный метод), чтобы случайно не сломать код, добавив ещё один абстрактный метод. В самих функциональных интерфейсах, перечисленных в статье, нет абсолютно ничего магического, это просто универсальные заготовки. А так любой интерфейс, в котором есть ровно один абстрактный метод, работает как функциональный (может инстанцироваться лямбда-выражением вместо анонимного класса), например, Comparator, который в джаве с версии 1.2.
                  0

                  Да я и говорю про эти интерфейсы, которые с одним методом. И про проверку компилятором, да. Я прекрасно это всё знаю, и всё это не отвечает на мой начальный вопрос.

                    +2

                    Ну всеже во первых обратная совместимость. У нас есть проект, который использует фреймворк ещё написанный на Java 1.4, и денег на миграцию не дают. Потому, что он до сих пор работает, хоть и убогий. Так вот ему можно скармливать как-бы-функциональные интерфейсы.

                    А во вторых, потом захотелось бы именованные типы, что бы не писать по сто раз String, String, String. Да и что бы понятнее было.

                    По мне так нормальное и красивое решение. И вам и нам так сказать.

              +1
              Объясните, зачем эти костыли с интерфейсами на каждый чих? Правда, что не осилили нормальные функциональные типы(например, (String, String) -> String вместо BiFunction) и кусок синтаксиса для их поддержки?

              Зачем добавлять новые отдельные типы, когда фича замечательно ложится на уже существующие интерфейсы с щепоткой синтаксического сахара?

                –2

                Ну чтобы не плодить интерфейсы... Просто когда начинаешь пользоваться нормальными типами, начинаешь думать, как бы это было, и вот имхо, было бы не очень.

              +8
              Очень странная статья, «Размышления о Java 8 и Java 11 в ожидании Java 17», после чего перечислили несколько новых возможностей Java 8 (довольно скомканно), вообще ничего не сказали ни про Java 11, ни про Java 17. Зачем они тогда в заголовке?

              Такое чувство, что у автора была статья про Java 8 лет десять назад, он добавил к ней пару абзацев, поменял заголовок и получил вроде бы актуальную модную статью. Зачем такое вообще переводить-то?
                +3
                А вы смотрите теги всегда. Если начинается с «Блог компании ...», значит скорее всего статья ни о чем, просто чтоб план выполнить.

                Очень жаль, что на Хабре нет черного списка, чтоб такие вот компании с их блогами в него загонять.
                  0
                  Есть механизм загнать рейтинг статьи ниже плинтуса, но такие статьи стабильно выше нуля. Значит либо есть потребители такого контента, либо работают механизмы накрутки.
                    0
                    Не всегда, в корпоротивных блогах тоже бывают интересные статья (да я сам писал несесколько статей в корп.блог).
                      +1

                      Вы правы, но откуда у этой статьи рейтинг 13?

                  +2

                  Бестолковая статья

                    –4

                    Бестолковая не статья а джава, и сравнение с водопадами и реками это как раз и есть "про джава" которая осталась в джава 8 а потом умерла а все что после "выходит" это игры с ностальгией или эксплуатация мавзолея.

                    Касательно интерфейсов - ООП - всё, его место где-то возле си++ а а прототипирования ему делать нечего, джависты это поняли уже окончательно и пихают как умеют функциональность в свое родное ООПэ

                    Хотя уже и еду понятно что все для чего могла быть нужна джава давно и на вселенную проще и полноценнее делает JS, но куда ж девать короны джава-чемпионов? И что сказать в молящие глаза упёртых адептов вскормленных годами запредельных зарплат за глубокомысленное смотрение в окно и думанье дум про объектную модель нанашего мега приложения подпирающего небо !?

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое