[Java, Kotlin] Pattern matching в Java 8
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Многие современные языки поддерживают сопоставление с образцом (pattern matching) на уровне языка.
Язык Java не является исключениям. И в Java 16 будет добавлено поддержка сопоставление с образцом для оператора instanceof, как финальной фичи.
В будущем надеемся, что сопоставление с образцом будем расширено и для других языковых конструкций.
Сопоставление с образцом раскрывают перед разработчиком возможность писать код более гибко и красивее, при этом оставляя его понятным.
Но что если нельзя перейти с тех или иных причин на новые версии Java. Благо используя возможности Java 8, можно реализовать некоторые возможности pattern matching в виде библиотеки.
Рассмотрим некоторые паттерны, и как их можна реализовать с помощью простенькой библиотеки.
Constant pattern позволяет проверить на равность с константами. В Java оператор switch позволяет проверить на равность числа, перечисления и строки. Но иногда хочется проверить на равность константы объектов используя метод equals().
switch (data) {
case new Person("man") -> System.out.println("man");
case new Person("woman") -> System.out.println("woman");
case new Person("child") -> System.out.println("child");
case null -> System.out.println("Null value ");
default -> System.out.println("Default value: " + data);
};
Подобный код можна написать следующим образом. При этом под капотом осуществляется сравнения значений и проверка их в операторе if. Можно использовать как форме утверждение так и как выражения.
Так же можно очень просто работать с диапазонами значений.
import static org.kl.jpml.pattern.ConstantPattern.*;
matches(data).as(
new Person("man"), () -> System.out.println("man");
new Person("woman"), () -> System.out.println("woman");
new Person("child"), () -> System.out.println("child");
Null.class, () -> System.out.println("Null value "),
Else.class, () -> System.out.println("Default value: " + data)
);
matches(data).as(
or(1, 2), () -> System.out.println("1 or 2");
in(3, 6), () -> System.out.println("between 3 and 6");
in(7), () -> System.out.println("7");
Null.class, () -> System.out.println("Null value "),
Else.class, () -> System.out.println("Default value: " + data)
);
Tuple pattern позволяет проверить на равность нескольких перемен с константами одновременно.
var (side, width) = border;
switch (side, width) {
case ("top", 25) -> System.out.println("top");
case ("bottom", 30) -> System.out.println("bottom");
case ("left", 15) -> System.out.println("left");
case ("right", 15) -> System.out.println("right");
case null -> System.out.println("Null value ");
default -> System.out.println("Default value ");
};
for ((side, width) : listBorders) {
System.out.println("border: " + [side + "," + width]);
}
При этом кроме использования в форме switch, можно разложить на сопоставляющие или пройти последовательно в цикле.
import static org.kl.jpml.pattern.TuplePattern.*;
let(border, (String side, int width) -> {
System.out.println("border: " + side + "," + width);
});
matches(side, width).as(
of("top", 25), () -> System.out.println("top");
of("bottom", 30), () -> System.out.println("bottom");
of("left", 15, () -> System.out.println("left");
of("right", 15), () -> System.out.println("right");
Null.class, () -> System.out.println("Null value"),
Else.class, () -> System.out.println("Default value")
);
foreach(listBorders, (String side, int width) -> {
System.out.println("border: " + side + "," + width);
}
Type test pattern позволяет одновременно сопоставить тип и извлечь значение переменной.
switch (data) {
case Integer i -> System.out.println(i * i);
case Byte b -> System.out.println(b * b);
case Long l -> System.out.println(l * l);
case String s -> System.out.println(s * s);
case null -> System.out.println("Null value ");
default -> System.out.println("Default value: " + data);
};
В Java для этого нам нужно сначала проверить тип, привести к типу и потом присвоить новой переменной. С помощью такого паттерна код стает на много проще.
import static org.kl.jpml.pattern.VerifyPattern.matches;
matches(data).as(
Integer.class, i -> { System.out.println(i * i); },
Byte.class, b -> { System.out.println(b * b); },
Long.class, l -> { System.out.println(l * l); },
String.class, s -> { System.out.println(s * s); },
Null.class, () -> { System.out.println("Null value "); },
Else.class, () -> { System.out.println("Default value: " + data); }
);
Guard pattern позволяет одновременно сопоставить тип и проверить на условия.
switch (data) {
case Integer i && i != 0 -> System.out.println(i * i);
case Byte b && b > -1 -> System.out.println(b * b);
case Long l && l < 5 -> System.out.println(l * l);
case String s && !s.empty() -> System.out.println(s * s);
case null -> System.out.println("Null value ");
default -> System.out.println("Default: " + data);
};
Подобную конструкцию можно реализовать следующим образом. Чтобы упростить написания условий, можно использовать следующее функции для сравнения: lessThan/lt, greaterThan/gt, lessThanOrEqual/le, greaterThanOrEqual/ge, equal/eq, notEqual/ne. А для того чтобы опустить условия можно пременить: always/yes, never/no.
import static org.kl.jpml.pattern.GuardPattern.matches;
matches(data).as(
Integer.class, i -> i != 0, i -> { System.out.println(i * i); },
Byte.class, b -> b > -1, b -> { System.out.println(b * b); },
Long.class, l -> l == 5, l -> { System.out.println(l * l); },
Null.class, () -> { System.out.println("Null value "); },
Else.class, () -> { System.out.println("Default value: " + data); }
);
matches(data).as(
Integer.class, ne(0), i -> { System.out.println(i * i); },
Byte.class, gt(-1), b -> { System.out.println(b * b); },
Long.class, eq(5), l -> { System.out.println(l * l); },
Null.class, () -> { System.out.println("Null value "); },
Else.class, () -> { System.out.println("Default value: " + data); }
);
Deconstruction pattern позволяет одновременно сопоставить тип и разложить объект на составляющие.
let (int w, int h) = figure;
switch (figure) {
case Rectangle(int w, int h) -> out.println("square: " + (w * h));
case Circle (int r) -> out.println("square: " + (2 * Math.PI * r));
default -> out.println("Default square: " + 0);
};
for ((int w, int h) : listFigures) {
System.out.println("square: " + (w * h));
}
В Java для этого нам нужно сначала проверить тип, привести к типу, присвоить новой переменной и только тогда через геттеры доступиться к полям класса.
import static org.kl.jpml.pattern.DeconstructPattern.*;
Figure figure = new Rectangle();
let(figure, (int w, int h) -> {
System.out.println("border: " + w + " " + h));
});
matches(figure).as(
Rectangle.class, (int w, int h) -> out.println("square: " + (w * h)),
Circle.class, (int r) -> out.println("square: " + (2 * Math.PI * r)),
Else.class, () -> out.println("Default square: " + 0)
);
foreach(listRectangles, (int w, int h) -> {
System.out.println("square: " + (w * h));
});
При этом чтобы получить составляющее, класс должен иметь один или несколько деконструирующих методов. Эти методы должны быть помечены аннотаций Extract.
Все параметры должны быть открытыми. Поскольку примитивы нельзя передать в метод по ссылке, нужно использовать обертки на примитивы IntRef, FloatRef и т.д.
Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory.
@Extract
public void deconstruct(IntRef width, IntRef height) {
width.set(this.width);
height.set(this.height);
}
Property pattern позволяет одновременно сопоставить тип и доступиться к полям класса по их именам.
let (w: int w, h:int h) = figure;
switch (figure) {
case Rectangle(w: int w == 5, h: int h == 10) -> out.println("sqr: " + (w * h));
case Rectangle(w: int w == 10, h: int h == 15) -> out.println("sqr: " + (w * h));
case Circle (r: int r) -> out.println("sqr: " + (2 * Math.PI * r));
default -> out.println("Default sqr: " + 0);
};
for ((w: int w, h: int h) : listRectangles) {
System.out.println("square: " + (w * h));
}
Это упрощенная форма деконструирующего паттерна, где нужны только конкретные поля класса разложить.
Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory.
import static org.kl.jpml.pattern.PropertyPattern.*;
Figure figure = new Rectangle();
let(figure, of("w", "h"), (int w, int h) -> {
System.out.println("border: " + w + " " + h));
});
matches(figure).as(
Rect.class, of("w", 5, "h", 10), (int w, int h) -> out.println("sqr: " + (w * h)),
Rect.class, of("w", 10, "h", 15), (int w, int h) -> out.println("sqr: " + (w * h)),
Circle.class, of("r"), (int r) -> out.println("sqr: " + (2 * Math.PI * r)),
Else.class, () -> out.println("Default sqr: " + 0)
);
foreach(listRectangles, of("x", "y"), (int w, int h) -> {
System.out.println("square: " + (w * h));
});
Также для упрощения именования полей можно использовать другой способ с ссылками на методы.
Figure figure = new Rect();
let(figure, Rect::w, Rect::h, (int w, int h) -> {
System.out.println("border: " + w + " " + h));
});
matches(figure).as(
Rect.class, Rect::w, Rect::h, (int w, int h) -> System.out.println("sqr: " + (w * h)),
Circle.class, Circle::r, (int r) -> System.out.println("sqr: " + (2 * Math.PI * r)),
Else.class, () -> System.out.println("Default sqr: " + 0)
);
foreach(listRectangles, Rect::w, Rect::h, (int w, int h) -> {
System.out.println("square: " + (w * h));
});
Position pattern позволяет одновременно сопоставить тип и проверить значение полей в порядке объявления.
switch (data) {
case Circle(5) -> System.out.println("small circle");
case Circle(15) -> System.out.println("middle circle");
case null -> System.out.println("Null value ");
default -> System.out.println("Default value: " + data);
};
В Java для этого нам нужно сначала проверить тип, привести к типу, присвоить новой переменной и только тогда через геттеры доступиться к полям класса и проверить на равность.
Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования.
import static org.kl.jpml.pattern.PositionPattern.*;
matches(data).as(
Circle.class, of(5), () -> { System.out.println("small circle"); },
Circle.class, of(15), () -> { System.out.println("middle circle"); },
Null.class, () -> { System.out.println("Null value "); },
Else.class, () -> { System.out.println("Default value: " + data); }
);
Также если разработчик не хочет проверять некоторые поля, эти поля должны быть помечены аннотаций Exclude. Эти поля должны быть объявлены последними.
class Circle {
private int radius;
@Exclude
private int temp;
}
Static pattern позволяет одновременно сопоставить тип и деконструировать объект используя фабричные методы.
switch (some) {
case Result.value(var v) -> System.out.println("value: " + v);
case Result.error(var e) -> System.out.println("error: " + e);
default -> System.out.println("Default value");
};
Подобный до деконструирующего паттерна, но имя деконструирующих методов, которые помеченные аннотаций Extract, должны быть указаны явно.
Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory.
import static org.kl.jpml.pattern.StaticPattern.*;
matches(figure).as(
Result.class, of("value"), (var v) -> System.out.println("value: " + v),
Result.class, of("error"), (var e) -> System.out.println("error: " + e),
Else.class, () -> System.out.println("Default value")
);
Sequence pattern позволяет проще обрабатывать последовательности данных.
List<Integer> list = ...;
switch (list) {
case empty() -> System.out.println("Empty value");
case head(var h) -> System.out.println("list head: " + h);
case tail(var t) -> System.out.println("list tail: " + t);
default -> System.out.println("Default value");
};
Используя библиотечные методы можно просто работать с последовательностями данных.
import static org.kl.jpml.pattern.SequencePattern.*;
List<Integer> list = List.of(1, 2, 3);
matches(figure,
empty() () -> System.out.println("Empty value"),
head(), (var h) -> System.out.println("list head: " + h),
tail(), (var t) -> System.out.println("list tail: " + t),
Else.class, () -> System.out.println("Default value")
);
Также для упрощения кода, можно использовать следующее функции, которые можно увидеть в современных языках как языковые фичи или функции.
import static org.kl.jpml.pattern.CommonPattern.*;
var rect = lazy(Rectangle::new);
var result = elvis(rect.get(), new Rectangle());
with(rect, it -> {
it.setWidth(5);
it.setHeight(10);
});
when(
side == Side.LEFT, () -> System.out.println("left value"),
side == Side.RIGHT, () -> System.out.println("right value")
);
repeat(3, () -> {
System.out.println("three time");
)
int even = self(number).takeIf(it -> it % 2 == 0);
int odd = self(number).takeUnless(it -> it % 2 == 0);
Как можно видеть pattern matching сильный инструмент, который намного упрощает написание кода. Используя возможности Java 8 можно сэмулировать возможности pattern matching самыми средствами языка.
Исходной код библиотеки можно посмотреть на github: link. Буду рад отзывам, предложениям по улучшению.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Java, Kotlin, Gradle, Микросервисы] Шаблон Kotlin микросервисов
- [Тестирование IT-систем, Программирование, Java, MongoDB] Run MongoDB Atlas locally for testing
- [JavaScript] Неудачный опыт миграции Electron приложения на ECMAScript модули
- [JavaScript, ReactJS] Поиск данных в столбцах таблицы с пагинацией (front-часть)
- [Java] Как использовать шаблон Circuit Breaker в приложении Spring Boot (перевод)
- [JavaScript, ReactJS, Карьера в IT-индустрии, TypeScript] Яндекс.Практикум запустил курс «React-разработчик»
- [Java, Анализ и проектирование систем, Промышленное программирование] Как катать релизы несколько раз в день и спать спокойно. Доклад Яндекса
- [Разработка мобильных приложений] Как выбрать мобильную кросс-платформу в 2021 году (перевод)
- [Java] Использование Google Protocol Buffers (protobuf) в Java (перевод)
- [Разработка веб-сайтов, JavaScript, Программирование, ReactJS] Разрабатываем чат на React с использованием Socket.IO
Теги для поиска: #_java, #_kotlin, #_java, #_kotlin, #_java, #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:40
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Многие современные языки поддерживают сопоставление с образцом (pattern matching) на уровне языка. Язык Java не является исключениям. И в Java 16 будет добавлено поддержка сопоставление с образцом для оператора instanceof, как финальной фичи. В будущем надеемся, что сопоставление с образцом будем расширено и для других языковых конструкций. Сопоставление с образцом раскрывают перед разработчиком возможность писать код более гибко и красивее, при этом оставляя его понятным. Но что если нельзя перейти с тех или иных причин на новые версии Java. Благо используя возможности Java 8, можно реализовать некоторые возможности pattern matching в виде библиотеки. Рассмотрим некоторые паттерны, и как их можна реализовать с помощью простенькой библиотеки. Constant pattern позволяет проверить на равность с константами. В Java оператор switch позволяет проверить на равность числа, перечисления и строки. Но иногда хочется проверить на равность константы объектов используя метод equals(). switch (data) {
case new Person("man") -> System.out.println("man"); case new Person("woman") -> System.out.println("woman"); case new Person("child") -> System.out.println("child"); case null -> System.out.println("Null value "); default -> System.out.println("Default value: " + data); }; Подобный код можна написать следующим образом. При этом под капотом осуществляется сравнения значений и проверка их в операторе if. Можно использовать как форме утверждение так и как выражения. Так же можно очень просто работать с диапазонами значений. import static org.kl.jpml.pattern.ConstantPattern.*;
matches(data).as( new Person("man"), () -> System.out.println("man"); new Person("woman"), () -> System.out.println("woman"); new Person("child"), () -> System.out.println("child"); Null.class, () -> System.out.println("Null value "), Else.class, () -> System.out.println("Default value: " + data) ); matches(data).as( or(1, 2), () -> System.out.println("1 or 2"); in(3, 6), () -> System.out.println("between 3 and 6"); in(7), () -> System.out.println("7"); Null.class, () -> System.out.println("Null value "), Else.class, () -> System.out.println("Default value: " + data) ); Tuple pattern позволяет проверить на равность нескольких перемен с константами одновременно. var (side, width) = border;
switch (side, width) { case ("top", 25) -> System.out.println("top"); case ("bottom", 30) -> System.out.println("bottom"); case ("left", 15) -> System.out.println("left"); case ("right", 15) -> System.out.println("right"); case null -> System.out.println("Null value "); default -> System.out.println("Default value "); }; for ((side, width) : listBorders) { System.out.println("border: " + [side + "," + width]); } При этом кроме использования в форме switch, можно разложить на сопоставляющие или пройти последовательно в цикле. import static org.kl.jpml.pattern.TuplePattern.*;
let(border, (String side, int width) -> { System.out.println("border: " + side + "," + width); }); matches(side, width).as( of("top", 25), () -> System.out.println("top"); of("bottom", 30), () -> System.out.println("bottom"); of("left", 15, () -> System.out.println("left"); of("right", 15), () -> System.out.println("right"); Null.class, () -> System.out.println("Null value"), Else.class, () -> System.out.println("Default value") ); foreach(listBorders, (String side, int width) -> { System.out.println("border: " + side + "," + width); } Type test pattern позволяет одновременно сопоставить тип и извлечь значение переменной. switch (data) {
case Integer i -> System.out.println(i * i); case Byte b -> System.out.println(b * b); case Long l -> System.out.println(l * l); case String s -> System.out.println(s * s); case null -> System.out.println("Null value "); default -> System.out.println("Default value: " + data); }; В Java для этого нам нужно сначала проверить тип, привести к типу и потом присвоить новой переменной. С помощью такого паттерна код стает на много проще. import static org.kl.jpml.pattern.VerifyPattern.matches;
matches(data).as( Integer.class, i -> { System.out.println(i * i); }, Byte.class, b -> { System.out.println(b * b); }, Long.class, l -> { System.out.println(l * l); }, String.class, s -> { System.out.println(s * s); }, Null.class, () -> { System.out.println("Null value "); }, Else.class, () -> { System.out.println("Default value: " + data); } ); Guard pattern позволяет одновременно сопоставить тип и проверить на условия. switch (data) {
case Integer i && i != 0 -> System.out.println(i * i); case Byte b && b > -1 -> System.out.println(b * b); case Long l && l < 5 -> System.out.println(l * l); case String s && !s.empty() -> System.out.println(s * s); case null -> System.out.println("Null value "); default -> System.out.println("Default: " + data); }; Подобную конструкцию можно реализовать следующим образом. Чтобы упростить написания условий, можно использовать следующее функции для сравнения: lessThan/lt, greaterThan/gt, lessThanOrEqual/le, greaterThanOrEqual/ge, equal/eq, notEqual/ne. А для того чтобы опустить условия можно пременить: always/yes, never/no. import static org.kl.jpml.pattern.GuardPattern.matches;
matches(data).as( Integer.class, i -> i != 0, i -> { System.out.println(i * i); }, Byte.class, b -> b > -1, b -> { System.out.println(b * b); }, Long.class, l -> l == 5, l -> { System.out.println(l * l); }, Null.class, () -> { System.out.println("Null value "); }, Else.class, () -> { System.out.println("Default value: " + data); } ); matches(data).as( Integer.class, ne(0), i -> { System.out.println(i * i); }, Byte.class, gt(-1), b -> { System.out.println(b * b); }, Long.class, eq(5), l -> { System.out.println(l * l); }, Null.class, () -> { System.out.println("Null value "); }, Else.class, () -> { System.out.println("Default value: " + data); } ); Deconstruction pattern позволяет одновременно сопоставить тип и разложить объект на составляющие. let (int w, int h) = figure;
switch (figure) { case Rectangle(int w, int h) -> out.println("square: " + (w * h)); case Circle (int r) -> out.println("square: " + (2 * Math.PI * r)); default -> out.println("Default square: " + 0); }; for ((int w, int h) : listFigures) { System.out.println("square: " + (w * h)); } В Java для этого нам нужно сначала проверить тип, привести к типу, присвоить новой переменной и только тогда через геттеры доступиться к полям класса. import static org.kl.jpml.pattern.DeconstructPattern.*;
Figure figure = new Rectangle(); let(figure, (int w, int h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure).as( Rectangle.class, (int w, int h) -> out.println("square: " + (w * h)), Circle.class, (int r) -> out.println("square: " + (2 * Math.PI * r)), Else.class, () -> out.println("Default square: " + 0) ); foreach(listRectangles, (int w, int h) -> { System.out.println("square: " + (w * h)); }); При этом чтобы получить составляющее, класс должен иметь один или несколько деконструирующих методов. Эти методы должны быть помечены аннотаций Extract. Все параметры должны быть открытыми. Поскольку примитивы нельзя передать в метод по ссылке, нужно использовать обертки на примитивы IntRef, FloatRef и т.д. Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory. @Extract
public void deconstruct(IntRef width, IntRef height) { width.set(this.width); height.set(this.height); } Property pattern позволяет одновременно сопоставить тип и доступиться к полям класса по их именам. let (w: int w, h:int h) = figure;
switch (figure) { case Rectangle(w: int w == 5, h: int h == 10) -> out.println("sqr: " + (w * h)); case Rectangle(w: int w == 10, h: int h == 15) -> out.println("sqr: " + (w * h)); case Circle (r: int r) -> out.println("sqr: " + (2 * Math.PI * r)); default -> out.println("Default sqr: " + 0); }; for ((w: int w, h: int h) : listRectangles) { System.out.println("square: " + (w * h)); } Это упрощенная форма деконструирующего паттерна, где нужны только конкретные поля класса разложить. Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory. import static org.kl.jpml.pattern.PropertyPattern.*;
Figure figure = new Rectangle(); let(figure, of("w", "h"), (int w, int h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure).as( Rect.class, of("w", 5, "h", 10), (int w, int h) -> out.println("sqr: " + (w * h)), Rect.class, of("w", 10, "h", 15), (int w, int h) -> out.println("sqr: " + (w * h)), Circle.class, of("r"), (int r) -> out.println("sqr: " + (2 * Math.PI * r)), Else.class, () -> out.println("Default sqr: " + 0) ); foreach(listRectangles, of("x", "y"), (int w, int h) -> { System.out.println("square: " + (w * h)); }); Также для упрощения именования полей можно использовать другой способ с ссылками на методы. Figure figure = new Rect();
let(figure, Rect::w, Rect::h, (int w, int h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure).as( Rect.class, Rect::w, Rect::h, (int w, int h) -> System.out.println("sqr: " + (w * h)), Circle.class, Circle::r, (int r) -> System.out.println("sqr: " + (2 * Math.PI * r)), Else.class, () -> System.out.println("Default sqr: " + 0) ); foreach(listRectangles, Rect::w, Rect::h, (int w, int h) -> { System.out.println("square: " + (w * h)); }); Position pattern позволяет одновременно сопоставить тип и проверить значение полей в порядке объявления. switch (data) {
case Circle(5) -> System.out.println("small circle"); case Circle(15) -> System.out.println("middle circle"); case null -> System.out.println("Null value "); default -> System.out.println("Default value: " + data); }; В Java для этого нам нужно сначала проверить тип, привести к типу, присвоить новой переменной и только тогда через геттеры доступиться к полям класса и проверить на равность. Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования. import static org.kl.jpml.pattern.PositionPattern.*;
matches(data).as( Circle.class, of(5), () -> { System.out.println("small circle"); }, Circle.class, of(15), () -> { System.out.println("middle circle"); }, Null.class, () -> { System.out.println("Null value "); }, Else.class, () -> { System.out.println("Default value: " + data); } ); Также если разработчик не хочет проверять некоторые поля, эти поля должны быть помечены аннотаций Exclude. Эти поля должны быть объявлены последними. class Circle {
private int radius; @Exclude private int temp; } Static pattern позволяет одновременно сопоставить тип и деконструировать объект используя фабричные методы. switch (some) {
case Result.value(var v) -> System.out.println("value: " + v); case Result.error(var e) -> System.out.println("error: " + e); default -> System.out.println("Default value"); }; Подобный до деконструирующего паттерна, но имя деконструирующих методов, которые помеченные аннотаций Extract, должны быть указаны явно. Чтобы уменьшить оверхед с использованием рефлексии, используется кеширования и прийомы с стандартным классом LambdaMetafactory. import static org.kl.jpml.pattern.StaticPattern.*;
matches(figure).as( Result.class, of("value"), (var v) -> System.out.println("value: " + v), Result.class, of("error"), (var e) -> System.out.println("error: " + e), Else.class, () -> System.out.println("Default value") ); Sequence pattern позволяет проще обрабатывать последовательности данных. List<Integer> list = ...;
switch (list) { case empty() -> System.out.println("Empty value"); case head(var h) -> System.out.println("list head: " + h); case tail(var t) -> System.out.println("list tail: " + t); default -> System.out.println("Default value"); }; Используя библиотечные методы можно просто работать с последовательностями данных. import static org.kl.jpml.pattern.SequencePattern.*;
List<Integer> list = List.of(1, 2, 3); matches(figure, empty() () -> System.out.println("Empty value"), head(), (var h) -> System.out.println("list head: " + h), tail(), (var t) -> System.out.println("list tail: " + t), Else.class, () -> System.out.println("Default value") ); Также для упрощения кода, можно использовать следующее функции, которые можно увидеть в современных языках как языковые фичи или функции. import static org.kl.jpml.pattern.CommonPattern.*;
var rect = lazy(Rectangle::new); var result = elvis(rect.get(), new Rectangle()); with(rect, it -> { it.setWidth(5); it.setHeight(10); }); when( side == Side.LEFT, () -> System.out.println("left value"), side == Side.RIGHT, () -> System.out.println("right value") ); repeat(3, () -> { System.out.println("three time"); ) int even = self(number).takeIf(it -> it % 2 == 0); int odd = self(number).takeUnless(it -> it % 2 == 0); Как можно видеть pattern matching сильный инструмент, который намного упрощает написание кода. Используя возможности Java 8 можно сэмулировать возможности pattern matching самыми средствами языка. Исходной код библиотеки можно посмотреть на github: link. Буду рад отзывам, предложениям по улучшению. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:40
Часовой пояс: UTC + 5