
Сравнение сопоставления узоров на разных языках: Java, Scala и многое другое
26 июля 2025 г.Сопоставление образцов является основной функцией разработки программного обеспечения. В то время как сопоставление рисунков применяется в нескольких местах, его текущее использование ограниченоswitch case
блоки. Я хочу сравнить силу соответствия шаблонов на нескольких языках программирования, с которыми я знаком в этом посте.
Я предполагаю, что каждый читатель знаком сswitch case
Синтаксис унаследован от C. Короче говоря:
- А
switch
Ссылка на пункт о возвращении стоимости заявления. - Каждый
case
Пункт устанавливает другое утверждение; Если значение соответствует оператору, оно выполняет соответствующий блок. case
Положения оцениваются по порядку. Первый пункт, который соответствует, получает свой блок.- .In c,
case
Положения проходят через; вам нужно явноbreak
чтобы избежатьswitch
, в противном случае, в следующемcase
оценивается
Образец Java
Я начну с Java, так как это был первый язык программирования, который я использовал в профессиональном контексте.
Сопоставление рисунков Java сильно развивалось в своих версиях. Вот «официальный» образец для версии 23, слегка справляется. Это определяет несколькоShape
реализации и аstatic
Метод оценки его периметра. Это определеннонетХороший пример объектно-ориентированного программирования.
interface Shape { }
record Rectangle(double length, double width) implements Shape { }
record Circle(double radius) implements Shape { }
public class Main {
static double getPerimeter(Shape s) throws IllegalArgumentException {
return switch (s) { //1
case Rectangle r when r.length() == r.width() -> { //2
System.out.println("Square detected");
yield 4 * r.length();
}
case Rectangle r -> //3
2 * r.length() + 2 * r.width();
case Circle c -> //4
2 * c.radius() * Math.PI;
default -> //5
throw new IllegalArgumentException("Unrecognized shape");
};
}
}
- Ссылка на
Shape
параметр какs
- Оценить, есть ли
s
является аRectangle
ибудь тоRectangle
это квадрат - Оценить, есть ли
s
является аRectangle
- Оценить, есть ли
s
является аCircle
- Если ни один из предыдущих положений не совпадает, по умолчанию бросить исключение
Эта версия Java будет нашей базовой линией.
Характеристики новогоswitch
Синтаксис
Новый синтаксис имеет некоторые преимущества по сравнению с Legacy unrrowswitch
унаследован от C.
C-стильcase
Положения проходят. Как только время выполнения выполнилаcase
Блок, он работает в следующемcase
блокировать. Чтобы избежать этого, вам нужно установитьbreak
явно. В не очень хорошие старые времена некоторые разработчики использовали эту функцию, чтобы избежать дублирования кода.
Тем не менее, опыт показал, что это было слишком большой причиной потенциальных ошибок: современные методы кодирования имеют тенденцию избегать этого. Тем не менее, слишком легко забытьbreak
Полем С новым синтаксисом стрел время выполнения только оценивает соответствующееcase
блокировать.
C-стильcase
Положения оценивают только простые значения. Сообщество отметило Java 7 как огромный благо, когда он разрешал струнные значения. Java 21 пошла еще дальше: в сочетании с синтаксисом стрелки, это позволило включить типы. В приведенном выше примере мы проверяем точноеShape
тип.
Кроме того, при включении классовиЕсли классовая иерархияsealed
Компилятор может автоматически обнаружить отсутствующие случаи. Наконец, версия 24 добавила дополнительную дополнительнуюwhen
фильтр.
С другой стороны, в старом синтаксисе С, время выполнения прыгает непосредственно на правильноеcase
пункт. Синтаксис новой стрелки оценивает их последовательно, точно так же, как если сif else
заявления.
В следующих разделах мы перенесем код на другие языки.
Сопоставление рисунка Скалы
Соответствие шаблона Скалы было непревзойденным с момента его создания. Котлин черпал из этого много вдохновения.
trait Shape
case class Rectangle(length: Double, width: Double) extends Shape
case class Circle(radius: Double) extends Shape
def getPerimeter(s: Shape) = {
s match { //1
case Rectangle(length, width) if length == width => //2-3
println("Square detected")
4 * length
case Rectangle(length, width) => 2 * length + 2 * width //3-4
case Circle(radius) => 2 * radius * Math.PI //3-5
case _ => throw new IllegalArgumentException("Unrecognized shape") //6
}
}
- Используйте
s
ссылка напрямую - Оценить, есть ли
s
является аRectangle
и будь тоRectangle
это квадрат - Scala дополнительно соответствует атрибутам класса
- Оценить, есть ли
s
является аRectangle
- Оценить, есть ли
s
является аCircle
- Если ни один из предыдущих положений не совпадает, по умолчанию бросить исключение
Сопоставление рисунка Котлина
Давайте переведем код Java в Котлин. Для этого мыдолженактивироватьэкспериментальныйXwhen-guards
Функция компиляции, описанная вKeep-371Полем
interface Shape
data class Rectangle(val length: Double, val width: Double): Shape
data class Circle(val radius: Double): Shape
fun getPerimeter(s: Shape) = when (s) { //1
is Rectangle if s.length == s.width -> { //2
println("Square detected")
4 * s.length
}
is Rectangle -> 2 * s.length + 2 * s.width //3
is Circle -> 2 * s.radius * Math.PI //4
else -> throw IllegalArgumentException("Unknown shape") //5
}
}
- Ссылка на
Shape
параметр какs
- Оценить, есть ли
s
является аRectangle
ибудь тоRectangle
S.width
равен егоheight
- Оценить, есть ли
s
является аRectangle
- Оценить, есть ли
s
является аCircle
- Если ни один из предыдущих положений не совпадает, по умолчанию бросить исключение
Узор Котлина, совпадающий, очень напоминает Java, с небольшими изменениями синтаксиса,напримерВif
заменяетwhen
Полем
Сравнительная эволюция Kotlin vs. Java в соответствии с узорами довольно поучительна. Java отстал за синтаксисом Legacy C, в то время как Kotlin уже мог совпадать с конкретными типами с самого начала. В 2020 году Java 14 сократила разрыв с оператором стрелы; Год спустя Java 16 полностью закрыла разрыв с возможностью соответствовать конкретным типам. Наконец, Java 23 добавилаwhen
пункт.
Экспериментальный характер котлинаif
означает, что Java обогнала Java - по крайней мере, в этой области! Это достаточно редко, чтобы быть отмеченным.
Паттерн Пайтона
Раньше Python не предлагал ничего похожего наswitch
Заявление вышеупомянутых языков JVM. Из версии 3.10 он предлагает такую же возможность элегантно:
class Shape:
pass
class Rectangle(Shape):
def __init__(self, length: float, width: float):
self.length = length
self.width = width
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def get_perimeter(s: Shape) -> float:
match s:
case Rectangle(length=l, width=w) if l == w:
print("Square detected")
return 4 * l
case Rectangle(length=l, width=w):
return 2 * l + 2 * w
case Circle(radius=r):
return 2 * pi * r
case _:
raise ValueError("Unknown shape")
Средство выполнения оцениваетcase
положения последовательно, как на языках JVM.
Растерный рисунок
Подход Rust к управлению памятью не хорошо играет с проверкой типов. Короче говоря, Rust предлагает две базовые концепции:
- Структуры являются структурированными заполнителями данных; Их размер памяти известен во время компиляции
- Черты - это контракты, сродни интерфейсам
- Вы можете предоставить реализацию черты для структуры
- Ссылка на переменную по его черту имеет последствия. Поскольку ссылка может указывать на структуры разных размеров, компилятор не может знать его размер во время компиляции и должен разместить переменную на кучу вместо стека.
Я попытался перенести оригинальный код в Rust One-One для образовательных целей. Оригинальная Java не использует полиморфизм; Это не здорово. В ржавчине это даже уроднее. Интересно понимать, что, хотя ржавчина не является объектно-ориентированным языком программирования, это заставляет вас использовать полиморфизм.
ПРЕДУПРЕЖДЕНИЕ: Я не рекомендую следующий код и буду отрицать какую -либо связь с ним!
trait Shape: Any {
fn as_any(&self) -> &dyn Any; //1
}
struct Circle {
radius: f64,
}
struct Rectangle {
width: f64,
height: f64,
}
impl Shape for Circle {
fn as_any(&self) -> &dyn Any { //2
self
}
}
impl Shape for Rectangle {
fn as_any(&self) -> &dyn Any { //2
self
}
}
fn get_perimeter(s: Box<dyn Shape>) -> f64 {
match s.as_any() { //3
any if any.is::<Rectangle>() => { //4
let rectangle = any.downcast_ref::<Rectangle>().unwrap(); //5
if rectangle.width == rectangle.height {
println!("Square matched");
4.0 * rectangle.width
} else {
2.0 * (rectangle.width + rectangle.height)
}
}
any if any.is::<Circle>() => { //4
let circle = any.downcast_ref::<Circle>().unwrap(); //5
2.0 * std::f64::consts::PI * circle.radius
}
_ => panic!()
}
}
- Система типа Rust не предлагает способ получить тип переменной. Мы должны создать специальную функцию для этого.
- Реализовать функцию для структур
- Сопоставление на основном типе структуры
- Проверьте тип
- Вниз до основной структуры, чтобы использовать свои поля
Этот искусственный кодовый порт выше искажает способности Rust's Pattern. Это относится кмного местПолем
Заключение
Среди всех языков, описанных в посте, Scala была первой, чтобы обеспечить сопоставление моделей вswitch
положения. В течение многих лет это был Грааль, с которым другие пытались догнать; Котлин и Ява наконец достигли этой стадии.
За пределами JVM, Python и Rust имеют мощные возможности для сопоставления узоров.
Благодаря разрушению, сопоставление шаблонов является огромной помощью для разработчиков, которые хотят написать читаемый и поддерживаемый код.
Полный исходный код для этого поста можно найти наGitHub^Полем
Идти дальше:
- Образец, соответствующий Java 23
- Сопоставление рисунков в котлине
- Сопоставление рисунков в Scala
- Заявление о матче в Python
- Паттерн, сопоставляющий в ржавчине
Первоначально опубликовано вJava Geek20 июля 2025 года
Оригинал