Методы расширения в Java

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


В таких языках программирования, как C#, Kotlin, Groovy, Scala есть возможность расширять класс путем добавления нового функционала, при этом не требуется наследование или изменение самого изначального класса. Это реализовано с помощью специальных выражений, называемых расширения. Java, в отличие от этих языков, не имеет такой возможности из коробки и даже не планирует в ближайших релизах. Благодаря Lombok это стало возможным. Методы расширения были реализованы в Lombok еще 8 лет назад (с поддержкой Eclipse), но для многих все упиралось в поддержку плагином в IDEA (код компилировался, но IDE его не распознавала как валидный). Lombok плагин теперь предустановлен в IDEA 2021.1 EAP, и теперь он поддерживает методы расширения lombok (спасибо akozlova, NekoCaffeine и mplushnikov).
Рассмотрим пример классического статического импорта:


import static org.apache.commons.lang3.StringUtils.capitalize;

public class ExtensionMethods {
    public static void main(String[] args) {
        String str = "test";
        String capitalized = capitalize(str);
        // "Test"
        System.out.println(capitalized);
    }
}

при переходе на метод расширения код станет выглядеть так:


import lombok.experimental.ExtensionMethod;
import org.apache.commons.lang3.StringUtils;

@ExtensionMethod(StringUtils.class)
public class ExtensionMethods {
    public static void main(String[] args) {
        String str = "test";
        String capitalized = str.capitalize();
        // "Test"
        System.out.println(capitalized);
    }
}

Заворачивания аргументов в скобки заменяются на цепочки вызовов, т.е. код вида call3(call2(call1(arg))) превратится в


arg.call1()
    .call2()
    .call3();

Во многих ситуациях это может облегчить чтение кода, особенно когда цепочки длинные, здесь есть некая аналогия со Stream Api или преобразования значения java.util.Optional.
Фактически это просто синтаксический сахар. Код при компиляции будет заменен на вызов статического метода. Первый аргумент статического метода и станет объектом "this".


null-значения


В отличие от обычных instance-методов, методы расширения могут работать и с null-значениями, т.е. подобный вызов вполне допустим:


import org.apache.commons.lang3.StringUtils;

@ExtensionMethod(StringUtils.class)
public class MethodExtensions {
    public static void main(String[] args) throws Exception {
        String nullStr = null;
        // "isEmpty=true"
        System.out.println("isEmpty=" + nullStr.trimToEmpty().isEmpty());
    }
}

Еще примеры


Можно добавить в проект на JDK 8 метод, который появится только в JDK 11:


@UtilityClass
public class CollectionExtensions {
    public static <T> T[] toArray(Collection<T> list, IntFunction<T[]> generator) {
        return list.stream().toArray(generator);
    }
}

@ExtensionMethod(CollectionExtensions.class)
public class MethodExtensions {
    public static void main(String[] args) throws Exception {
        List<Integer> list = Arrays.asList(1, 2, 3);
        // toArray(IntFunction<T[]>) добавлен только в Java 11
        Integer[] array = list.toArray(Integer[]::new);
        // "[1, 2, 3]"
        System.out.println(Arrays.toString(array));
    }
}

Или добавить более лаконичный вызов Stream.collect(toList()):


@UtilityClass
public class StreamExtensions {
    public static <T> List<T> toList(Stream<T> stream) {
        return stream.collect(Collectors.toList());
    }
}

@ExtensionMethod(CollectionExtensions.class)
public class MethodExtensions {
    public static void main(String[] args) throws Exception {
        List<Integer> list = Arrays.asList(3, 1, 2);
        List<Integer> sorted = list.stream()
                .sorted()
                .toList();

        // "[1, 2, 3]"
        System.out.println(sorted);
    }
}

Настройка проекта


  • Установите последнюю версию IDEA EAP, важно: EAP версии не стабильны, зато бесплатны. Плагин доступен и в Ultimate, и в Community Edition.
  • Добавьте зависимость lombok: maven

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
    <scope>provided</scope>
</dependency>

либо для gradle:


compileOnly 'org.projectlombok:lombok:1.18.16'
annotationProcessor 'org.projectlombok:lombok:1.18.16'
testCompileOnly 'org.projectlombok:lombok:1.18.16'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.16'

  • Убедитесь, что включена опция проекта Build, Execution, Deployment -> Compiler -> Annotations processor -> Enable annotation processing
  • Добавьте аннотацию @ExtensionMethod на класс (откуда будет вызов), перечисляя все утилитные классы, из которых необходимо импортировать вызовы.

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

Что думаете про методы расширения?

  • 33,3%Уже использую в Kotlin, Groovy, Scala, etc.3
  • 55,6%Уже использую в Lombok / обязательно попробую5
  • 0,0%Использую Lombok, но идея расширений не нравится0
  • 11,1%Lombok — зло1
  • 0,0%Другое (напишите в комментарии)0
Источник: https://habr.com/ru/post/527688/


Интересные статьи

Интересные статьи

После вводной статьи про Spark Shell мне хотелось бы рассмотреть некоторые приемы оптимизации запросов в Spark и Hive, и сравнить их с теми, что обычно применяются в классических СУБД тип...
За свою карьеру я успел поработать со множеством языков программирования. Писал flash-игры на ActionScript 3 и Android-игры на Java, сервера на Java, Scala и NodeJS (Java...
Сегодня в свет вышла новая, 15-я версия платформы Java. Скачать JDK 15 можно по следующим ссылкам: Oracle JDK (проприетарная версия, обратите внимание на ограничения в использован...
И снова привет! В прошлом посте мы начали рассматривать процесс управления зависимостями в JavaScript, разобрали основы: что такое npm-пакет; как выглядит манифест пакета; в каких п...
Всем привет. Все меньше времени остается до запуска курса «Безопасность информационных систем», поэтому сегодня мы продолжаем делиться публикациями, приуроченными к запуску данного курса. Кстати,...