Рубрики
Без рубрики

Введение в Derive4J

Краткий и практический обзор Derive4j.

Автор оригинала: baeldung.

1. Введение

Derive4J является аннотации процессор, который позволяет различные функциональные концепции в Java 8.

В этом учебнике мы представим Derive4J и наиболее важные концепции, включенные в рамках:

  • Типы алгебраических данных
  • Структурная сопоставление шаблонов
  • Лень первого класса

2. Зависимость от Maven

Чтобы использовать Derive4J, мы должны включить зависимость к нашему проекту:


    org.derive4j
    derive4j
    1.1.0
    true

3. Типы алгебраических данных

3.1. Описание объекта

Алгебраические типы данных (АДТ) являются своего рода составным типом – они являются комбинациями других типов или дженериков.

АДТ обычно подпадают под две основные категории:

  • сумма
  • продукт

Алгебраические типы данных присутствуют по умолчанию на многих языках, таких как Haskell и Scala.

3.2. Тип суммы

Sum – это тип данных, представляющий логическую операцию OR. Это означает, что это может быть одно или другое, но не оба из них. Проще говоря, тип суммы – это набор различных случаев. Название “сумма” происходит от того факта, что общее число различных значений является общее число случаев.

Энум это самое близкое в Java к типу суммы. Энум имеет набор возможных значений, но может иметь только один из них за один раз. Тем не менее, мы не можем связать какие-либо дополнительные данные с Энум на Java, что является основным преимуществом алгебраических типов данных по сравнению с Энум .

3.3. Тип продукта

Продукт представляет собой тип данных, представляющий логическую и операцию. Это сочетание нескольких значений.

Класс в Java можно рассматривать как тип продукта. Типы продуктов определяются сочетанием их полей в целом.

Мы можем найти более подробную информацию о ADTs в этой статье Википедии .

3.4. Использование

Одним из наиболее часто используемых типов алгебраических данных является каждый. Мы можем думать об Либо как более сложный Необязательный которые могут быть использованы при возможности отсутствия значений или операция может привести к исключению.

Нам нужно аннотировать абстрактный класс или интерфейс по крайней мере одним абстрактным методом, который будет использоваться Derive4J для создания структуры нашего ADT.

Создать Либо тип данных в Derive4J нам нужно создать интерфейс :

@Data
interface Either {
     X match(Function left, Function right);
}

Наша интерфейс аннотируется @Data , что позволит Derive4J создать правильный код для нас. Сгенерированный код содержит заводские методы, ленивые конструкторы и различные другие методы.

По умолчанию генерируемый код получает имя аннотированного класс , но во множественном числе. Но, есть возможность настроить, что через inClass параметр.

Теперь мы можем использовать сгенерированный код для создания Либо ADT и убедитесь, что он работает должным образом:

public void testEitherIsCreatedFromRight() {
    Either either = Eithers.right("Okay");
    Optional leftOptional = Eithers.getLeft(either);
    Optional rightOptional = Eithers.getRight(either);
    Assertions.assertThat(leftOptional).isEmpty();
    Assertions.assertThat(rightOptional).hasValue("Okay");
}

Мы также можем использовать сгенерированный матч () метод выполнения функции в зависимости от того, с какой Либо присутствует:

public void testEitherIsMatchedWithRight() {
    Either either = Eithers.right("Okay");
    Function leftFunction = Mockito.mock(Function.class);
    Function rightFunction = Mockito.mock(Function.class);
    either.match(leftFunction, rightFunction);
    Mockito.verify(rightFunction, Mockito.times(1)).apply("Okay");
    Mockito.verify(leftFunction, Mockito.times(0)).apply(Mockito.any(Exception.class));
}

4. Сопоставление шаблонов

Одной из особенностей, включенных с помощью алгебраических типов данных, является Шаблону.

Сопоставление шаблонов является механизмом проверки значения против шаблона. В принципе, сопоставление шаблонов является более мощным переключить заявление, но без ограничений на соответствующий тип или требование о том, чтобы шаблоны были постоянными. Для получения дополнительной информации, мы можем проверить эта статья Википедии о шаблоне соответствия .

Чтобы использовать сопоставление шаблонов, мы создадим класс, который смоет запрос HTTP. Пользователи смогут использовать один из данных методов HTTP:

  • Получить
  • Поместить
  • удалить
  • класть

Давайте смоем наш класс запросов как ADT в Derive4J, начиная с HTTPRequest интерфейс:

@Data
interface HTTPRequest {
    interface Cases{
        R GET(String path);
        R POST(String path);
        R PUT(String path);
        R DELETE(String path);
    }

     R match(Cases method);
}

Сгенерированный класс, HttpRequests (обратите внимание на форму множественного числа), теперь позволит нам выполнять сопоставление шаблонов в зависимости от типа запроса.

Для этого мы создадим очень простой HTTPServer класс, который будет отвечать с различными Статус в зависимости от типа запроса.

Во-первых, давайте создадим простую HTTPРеспонс класс, который будет служить в качестве ответа от нашего сервера к нашему клиенту:

public class HTTPResponse {
    int statusCode;
    String responseBody;

    public HTTPResponse(int statusCode, String responseBody) {
        this.statusCode = statusCode;
        this.responseBody = responseBody;
    }
}

Затем мы можем создать сервер, который будет использовать шаблон соответствия для отправки надлежащего ответа:

public class HTTPServer {
    public static String GET_RESPONSE_BODY = "Success!";
    public static String PUT_RESPONSE_BODY = "Resource Created!";
    public static String POST_RESPONSE_BODY = "Resource Updated!";
    public static String DELETE_RESPONSE_BODY = "Resource Deleted!";

    public HTTPResponse acceptRequest(HTTPRequest request) {
        return HTTPRequests.caseOf(request)
          .GET((path) -> new HTTPResponse(200, GET_RESPONSE_BODY))
          .POST((path,body) -> new HTTPResponse(201, POST_RESPONSE_BODY))
          .PUT((path,body) -> new HTTPResponse(200, PUT_RESPONSE_BODY))
          .DELETE(path -> new HTTPResponse(200, DELETE_RESPONSE_BODY));
    }
}

принятьРеквест () метод нашего класс использует шаблон, соответствующий типу запроса, и возвращает различные ответы в зависимости от типа запроса:

@Test
public void whenRequestReachesServer_thenProperResponseIsReturned() {
    HTTPServer server = new HTTPServer();
    HTTPRequest postRequest = HTTPRequests.POST("http://test.com/post", "Resource");
    HTTPResponse response = server.acceptRequest(postRequest);
    Assert.assertEquals(201, response.getStatusCode());
    Assert.assertEquals(HTTPServer.POST_RESPONSE_BODY, response.getResponseBody());
}

5. Лень первого класса

Derive4J позволяет нам ввести понятие лени, а это означает, что наши объекты не будут инициализированы, пока мы не выполнить операцию на них. Давайте объявим интерфейс как ЛенивыйРеквест и настроить сгенерированный класс для ЛенивыйРеквестИмпль :

@Data(value = @Derive(
  inClass = "{ClassName}Impl",
  make = {Make.lazyConstructor, Make.constructors}
))
public interface LazyRequest {
    interface Cases{
        R GET(String path);
        R POST(String path, String body);
        R PUT(String path, String body);
        R DELETE(String path);
    }

     R match(LazyRequest.Cases method);
}

Теперь мы можем проверить, работает ли созданный ленивый конструктор так, как должен:

@Test
public void whenRequestIsReferenced_thenRequestIsLazilyContructed() {
    LazyRequestSupplier mockSupplier = Mockito.spy(new LazyRequestSupplier());
    LazyRequest request = LazyRequestImpl.lazy(() -> mockSupplier.get());
    Mockito.verify(mockSupplier, Mockito.times(0)).get();
    Assert.assertEquals(LazyRequestImpl.getPath(request), "http://test.com/get");
    Mockito.verify(mockSupplier, Mockito.times(1)).get();
}

class LazyRequestSupplier implements Supplier {
    @Override
    public LazyRequest get() {
        return LazyRequestImpl.GET("http://test.com/get");
    }
}

Мы можем найти более подробную информацию о лени первого класса и в документации Scala .

6. Заключение

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

Мы можем найти более подробную информацию о библиотеке можно найти в официальной документации Derive4J .

Как всегда, все образцы кода можно найти более чем на GitHub.