{"id":15069063,"url":"https://github.com/klappdev/jpml","last_synced_at":"2025-04-10T17:44:08.501Z","repository":{"id":176314130,"uuid":"171348128","full_name":"klappdev/jpml","owner":"klappdev","description":"Java pattern matching library","archived":false,"fork":false,"pushed_at":"2024-08-03T06:22:08.000Z","size":2905,"stargazers_count":19,"open_issues_count":2,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-24T15:22:03.410Z","etag":null,"topics":["functional-programming","java","java-11","java-8","pattern-matching"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klappdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-18T20:02:54.000Z","updated_at":"2025-02-28T19:12:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"90b0eafe-48e1-4699-9c36-1f43cdfd98b4","html_url":"https://github.com/klappdev/jpml","commit_stats":null,"previous_names":["klappdev/jpml"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klappdev%2Fjpml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klappdev%2Fjpml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klappdev%2Fjpml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klappdev%2Fjpml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klappdev","download_url":"https://codeload.github.com/klappdev/jpml/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248262296,"owners_count":21074283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","java","java-11","java-8","pattern-matching"],"created_at":"2024-09-25T01:40:17.621Z","updated_at":"2025-04-10T17:44:08.484Z","avatar_url":"https://github.com/klappdev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jpml\n**J**ava **p**attern **m**atching **l**ibrary.\n\n[![Apk file](https://img.shields.io/github/downloads/genuinetools/apk-file/total.svg?style=for-the-badge)](files/jpml-jvm8.jar)\n\nMany languages support pattern matching at the language level. \u003cbr/\u003e\nJava language does not support at the moment pattern matching  \u003cbr/\u003e\nbut there is hope that in the future everything will be change.\n\nUsing Java 8 features, we can emulate some of the features pattern matching.\u003cbr/\u003e\nIn general, patterns can be divided into three types: constant, type, var.\u003cbr/\u003e\nThe library supports both statement and expression.\u003cbr/\u003e\n\n**Constant pattern** allow test for equality with constants.\n\n```Java\n   switch (data) {\n      case new Person(\"man\")    -\u003e System.out.println(\"man\");\n      case new Person(\"woman\")  -\u003e System.out.println(\"woman\");\n      case new Person(\"child\") \t-\u003e System.out.println(\"child\");        \n      case null                 -\u003e System.out.println(\"Null value \");\n      default                   -\u003e System.out.println(\"Default value: \" + data);\n   };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.ConstantPattern.match;\n\n   match(data).as(\n      new Person(\"man\"),    () -\u003e  System.out.println(\"man\"),\n      new Person(\"woman\"),  () -\u003e  System.out.println(\"woman\"),\n      new Person(\"child\"),  () -\u003e  System.out.println(\"child\"),        \n      Null.class,           () -\u003e  System.out.println(\"Null value \"),\n      Else.class,           () -\u003e  System.out.println(\"Default value: \" + data)\n   );\n```\n\nFor work with range values could use such functions: in/or.\n\n```Java\n   import static org.kl.jpml.pattern.ConstantPattern.or;\n   import static org.kl.jpml.pattern.ConstantPattern.in; \n   \n   match(data).as(\n      or(1, 2),    () -\u003e  System.out.println(\"1 or 2\"),\n      in(3, 6),    () -\u003e  System.out.println(\"between 3 and 6\"),\n      in(7),       () -\u003e  System.out.println(\"7\"),        \n      Null.class,  () -\u003e  System.out.println(\"Null value \"),\n      Else.class,  () -\u003e  System.out.println(\"Default value: \" + data)\n   );\n```\n\n**Tuple pattern** allow test for equality multiple pieces with constants.\n\n```Java\n   let (side, width) = border;\t\n\n   switch (side, width) {\n      case (\"top\",    25) -\u003e System.out.println(\"top\");\n      case (\"bottom\", 30) -\u003e System.out.println(\"bottom\");\n      case (\"left\",   15) -\u003e System.out.println(\"left\");        \n      case (\"right\",  15) -\u003e System.out.println(\"right\");\n      default           -\u003e System.out.println(\"Default value \");\n   };\n   \n   for ((side, width) : listBorders) {\n\t  System.out.println(\"border: \" + [side + \",\" + width]); \t\n   }\n```\nUsing this library developer can write in the following way. Using Java 11 feature, could\ndeduce types parameters. \n\n```Java\n   import static org.kl.jpml.pattern.TuplePattern.match;\n   import static org.kl.jpml.pattern.TuplePattern.let;\n\n   let(border, (String side, int width) -\u003e {\n      System.out.println(\"border: \" + side + \",\" + width);\n   });\n   \n   match(side, width).as(\n      of(\"top\",    25),  () -\u003e System.out.println(\"top\"),\n      of(\"bottom\", 30),  () -\u003e System.out.println(\"bottom\"),\n      of(\"left\",   15),  () -\u003e System.out.println(\"left\"),        \n      of(\"right\",  15),  () -\u003e System.out.println(\"right\"),\n      Else.class,    () -\u003e System.out.println(\"Default value\")\n   );\n   \n   foreach(listBorders, (String side, int width) -\u003e {\n\t  System.out.println(\"border: \" + side + \",\" + width); \t\n   }\n```\n\n**Type test pattern** allow match type and then extract value.\n\n```Java\n    switch (data) {\n      case Integer i  -\u003e System.out.println(i * i);\n      case Byte    b  -\u003e System.out.println(b * b);\n      case Long    l  -\u003e System.out.println(l * l);        \n      case String  s  -\u003e System.out.println(s * s);\n      case null       -\u003e System.out.println(\"Null value \");\n      default         -\u003e System.out.println(\"Default value: \" + data);\n   };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.TypeTestPattern.match;\n\n   match(data).as(\n      Integer.class, i  -\u003e { System.out.println(i * i); },\n      Byte.class,    b  -\u003e { System.out.println(b * b); },\n      Long.class,    l  -\u003e { System.out.println(l * l); },\n      String.class,  s  -\u003e { System.out.println(s * s); },\n      Null.class,    () -\u003e { System.out.println(\"Null value \"); },\n      Else.class,    () -\u003e { System.out.println(\"Default value: \" + data); }\n   );\n```\n\n**Exhaustive pattern** allow match exhaustive subclasses type.\n\n```Java\n   public sealed class Result\u003cT, E extends Throwable\u003e {\n      public class Success\u003cT\u003e extends Result\u003cT, E\u003e { \n         Success(T value) {} \n      }\n      public class Failure\u003cE\u003e extends Result\u003cT, E\u003e {\n         Failture(E exception) {}\n      }\n   }\n\n   switch (result) {\n      case Success  s -\u003e System.out.println(\"Success:  \" + s),\n      case Failture f -\u003e System.out.println(\"Failture: \" + f)\n   };\n```\n\nUsing this library developer can write in the following way.\n\t\n```Java\n    import static org.kl.jpml.pattern.ExhaustivePattern.match;\n   \n    @Sealed\n    public abstract class Result\u003cT, E extends Throwable\u003e {\n       private Result() {}\n   \n       public static class Success\u003cT\u003e extends Result\u003cT, E\u003e { \n\t  Success(T value) {} \n       }\n       public static class Failure\u003cE\u003e extends Result\u003cT, E\u003e {\n\t  Failture(E exception) {}\n       }\n    }\n\t\n    match(result).as(\n       Result.Success.class,  s -\u003e System.out.println(\"Success:  \" + s),\n       Result.Failture.class, f -\u003e System.out.println(\"Success:  \" + s)\n    );       \n```\n\n**Guard pattern** allow match type and check condition for the truth at one time.\n\n```Java\n    switch (data) {\n      case Integer i \u0026\u0026 i != 0     -\u003e System.out.println(i * i);\n      case Byte    b \u0026\u0026 b \u003e -1     -\u003e System.out.println(b * b);\n      case Long    l \u0026\u0026 l \u003c 5      -\u003e System.out.println(l * l);\n      case String  s \u0026\u0026 !s.empty() -\u003e System.out.println(s * s);\n      case null                    -\u003e System.out.println(\"Null value \");\n      default                      -\u003e System.out.println(\"Default value: \" + data);\n   };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.GuardPattern.match;\n\n   match(data).as(           \n      Integer.class, i  -\u003e i != 0,  i  -\u003e { System.out.println(i * i); },\n      Byte.class,    b  -\u003e b \u003e -1,  b  -\u003e { System.out.println(b * b); },\n      Long.class,    l  -\u003e l == 5,  l  -\u003e { System.out.println(l * l); },\n      Null.class,    () -\u003e { System.out.println(\"Null value \"); },\n      Else.class,    () -\u003e { System.out.println(\"Default value: \" + data); }\n   );\n```\n\nFor simplify writing a condition, developer can use such functions to compare: \u003cbr/\u003e\nlessThan/lt, greaterThan/gt, lessThanOrEqual/le, greaterThanOrEqual/ge,  \u003cbr/\u003e\nequal/eq, notEqual/ne. Also for omit condition could use such functions:\u003cbr/\u003e\nalways/yes, never/no.\n\n```Java\n   match(data).as(           \n      Integer.class, ne(0),  i  -\u003e { System.out.println(i * i); },\n      Byte.class,    gt(-1), b  -\u003e { System.out.println(b * b); },\n      Long.class,    eq(5),  l  -\u003e { System.out.println(l * l); },\n      Null.class,    () -\u003e { System.out.println(\"Null value \"); },\n      Else.class,    () -\u003e { System.out.println(\"Default value: \" + data); }\n   );\n```\n\n**Deconstruction pattern** allow match type and deconstruct object at the parts.\n\n```Java\n   Figure figure = new Rectangle();\n \n   let (int w, int h) = figure;\n \n   switch (figure) {\n      case Rectangle(int w, int h) -\u003e System.out.println(\"square: \" + (w * h));\n      case Circle   (int r)        -\u003e System.out.println(\"square: \" + (2 * Math.PI * r));\n      default                      -\u003e System.out.println(\"Default square: \" + 0);\n   };\n   \n   for ((int w, int h) :  listRectangles) {\n      System.out.println(\"square: \" + (w * h));\n   }\n```\n\nUsing this library developer can write in the following way. Using Java 11 feature, we can deduce\ntypes deconstruct parameters.\n\n```Java\n   import static org.kl.jpml.pattern.DeconstructPattern.match;\n   import static org.kl.jpml.pattern.DeconstructPattern.foreach;\n   import static org.kl.jpml.pattern.DeconstructPattern.let;\n\n   Figure figure = new Rectangle();\n\n   let(figure, (int w, int h) -\u003e {\n      System.out.println(\"border: \" + w + \" \" + h));\n   });\n\n   match(figure).as(\n      Rectangle.class, (int w, int h) -\u003e System.out.println(\"square: \" + (w * h)),\n      Circle.class,    (int r)        -\u003e System.out.println(\"square: \" + (2 * Math.PI * r)),\n      Else.class,      ()             -\u003e System.out.println(\"Default square: \" + 0)\n   );\n   \n   foreach(listRectangles, (int w, int h) -\u003e {\n      System.out.println(\"square: \" + (w * h));\n   });\n```\n\nDeconstruct classes must to have one or more extract method(s). \u003cbr/\u003e\nThey must to be marked annotation @Extract. Parameters must to be \u003cbr/\u003e\noutput. Since primitive and wrappers for primitive types can't to be \u003cbr/\u003e\npass by reference we must to use wrappers such IntRef, FloatRef and etc.\n\n```Java\n   @Extract\n   public void deconstruct(IntRef width, IntRef height) {\n      width.set(this.width);\n      height.set(this.height);\n   }\n```\n**Property pattern** allow match type and access to fields class.\n```Java\n    Figure figure = new Rectangle();\n    \n    let (w: int w, h:int h) = figure;\n \n    switch (figure) {\n      case Rectangle(w: int w == 5,  h: int h == 10) -\u003e System.out.println(\"sqr: \" + (w * h));\n      case Rectangle(w: int w == 10, h: int h == 15) -\u003e System.out.println(\"sqr: \" + (w * h));\n      case Circle   (r: int r) -\u003e System.out.println(\"sqr: \" + (2 * Math.PI * r));\n      default                  -\u003e System.out.println(\"Default sqr: \" + 0);\n   };\n   \n   for ((w: int w, h: int h) :  listRectangles) {\n      System.out.println(\"square: \" + (w * h));\n   }\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.PropertyPattern.match;\n   import static org.kl.jpml.pattern.PropertyPattern.foreach;\n   import static org.kl.jpml.pattern.PropertyPattern.let;\n   import static org.kl.jpml.pattern.PropertyPattern.of;   \n\n   Figure figure = new Rectangle();\n\n   let(figure, of(\"w\", \"h\"), (int w, int h) -\u003e {\n      System.out.println(\"border: \" + w + \" \" + h));\n   });\n\n   match(figure).as(\n      Rect.class,    of(\"w\", 5,  \"h\", 10), (int w, int h) -\u003e System.out.println(\"sqr: \" + (w * h)),\n      Rect.class,    of(\"w\", 10, \"h\", 15), (int w, int h) -\u003e System.out.println(\"sqr: \" + (w * h)),\n      Circle.class,  of(\"r\"), (int r)  -\u003e System.out.println(\"sqr: \" + (2 * Math.PI * r)),\n      Else.class,    ()                -\u003e System.out.println(\"Default sqr: \" + 0)\n   );\n   \n   foreach(listRectangles, of(\"x\", \"y\"), (int w, int h) -\u003e {\n      System.out.println(\"square: \" + (w * h));\n   });\n```\n\nFor simplify naming parameters could use another way. Using Java 11 feature, we can deduce types\nproperty parameters.\n\n```Java\n   Figure figure = new Rect();\n\n   let(figure, Rect::w, Rect::h, (var w, var h) -\u003e {\n      System.out.println(\"border: \" + w + \" \" + h));\n   });\n\n   match(figure).as(\n      Rect.class,    Rect::w, Rect::h, (var w, var h) -\u003e System.out.println(\"sqr: \" + (w * h)),\n      Circle.class,  Circle::r, (var r)  -\u003e System.out.println(\"sqr: \" + (2 * Math.PI * r)),\n      Else.class,    ()                  -\u003e System.out.println(\"Default sqr: \" + 0)\n   );\n   \n   foreach(listRectangles, Rect::w, Rect::h, (var w, var h) -\u003e {\n      System.out.println(\"square: \" + (w * h));\n   });\n```\n\n**Position pattern** allow match type and check value fields class in order of declaration.\n\n```Java\n    switch (data) {\n      case Circle(5)   -\u003e System.out.println(\"small circle\");\n      case Circle(15)  -\u003e System.out.println(\"middle circle\");\n      case null        -\u003e System.out.println(\"Null value \");\n      default          -\u003e System.out.println(\"Default value: \" + data);\n   };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.PositionPattern.match;\n   import static org.kl.jpml.pattern.PositionPattern.of;\n\n   match(data).as(           \n      Circle.class,  of(5),  () -\u003e { System.out.println(\"small circle\"); },\n      Circle.class,  of(15), () -\u003e { System.out.println(\"middle circle\"); },\n      Null.class,            () -\u003e { System.out.println(\"Null value \"); },\n      Else.class,            () -\u003e { System.out.println(\"Default value: \" + data); }\n   );\n```\n\nIf developer does not want check some fields class that fields must \u003cbr/\u003e\nto be marked with annotation @Exclude. Excluded fields must to be declared last.\n\n```Java\n   class Circle {\n      private int radius;\n      \t  \n      @Exclude\n      private int temp;\n   }\n```\n\n**Static pattern** allow match type and deconstruct object using factory methods.\n\n```Java   \n   switch (some) {\n      case Rect.unapply(int w, int h) -\u003e System.out.println(\"square: \" + (w * h));\n      case Circle.unapply(int r)      -\u003e System.out.println(\"square: \" + (2 * Math.PI * r));\n      default -\u003e System.out.println(\"Default square: \" + 0);\n   };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   import static org.kl.jpml.pattern.StaticPattern.match;\n\n   match(figure).as(\n      Rect.class,   of(\"unapply\"), (int w, int h) -\u003e out.println(\"square: \" + (w * h)),\n      Circle.class, of(\"unapply\"), (int r)        -\u003e out.println(\"square: \" + (2 * Math.PI * r)),\n      Else.class, ()                              -\u003e out.println(\"Default square: \" + 0)\n   );    \n```\n\nAlso this pattern give simplify work with Optional\u003c\u003cV\u003e\u003e.\n\t\n```Java\n   match(value).as(\n      Optional::empty, () -\u003e out.println(\"empty\"),\n      Optional::get,    v -\u003e out.println(\"value: \" + v)\n   );\n```\n\n**Sequence pattern** allow processing on data sequence.\n\n```Java\n   List\u003cInteger\u003e list = List.of(1, 2, 3);\n  \n   switch (list) {\n      case empty()       -\u003e System.out.println(\"Empty value\");\n      case head(var h)   -\u003e System.out.println(\"list head: \" + h);\n      case middle(var m) -\u003e out.println(\"middle list:\" + m);\n      case tail(var t)   -\u003e out.println(\"tail list:  \" + t);\n      case at(1, var i)  -\u003e out.println(\"at list:    \" + i);\n      case edges(var f, var l) -\u003e out.println(\"edges: \" + f + \" - \" + l);      \n      default            -\u003e System.out.println(\"Default value\");\n   };\n```\n\nUsing this library developer can write in the following way. Using Java 11 feature, we can deduce\ntypes property parameters. \n\n```Java\n   import static org.kl.jpml.pattern.SequencePattern.match;\n\n   List\u003cInteger\u003e list = List.of(1, 2, 3);\n\n   match(figure).as(\n      empty()   () -\u003e System.out.println(\"Empty value\"),\n      head(),   (int h) -\u003e System.out.println(\"list head: \" + h),\n      middle(), (int m) -\u003e out.println(\"middle list:\" + m),\n      tail(),   (int t) -\u003e out.println(\"tail list:  \" + t),\n      at(1),    (int i) -\u003e out.println(\"at list:    \" + i),\n      edges(),  (int f, int l) -\u003e out.println(\"edges: \" + f + \" - \" + l),\n      Else.class, () -\u003e System.out.println(\"Default value\")\n   );   \n```\n\n**Common pattern** contains general constructions which could be useful.\n\n```Java \n    lazy var rect = new Rectangle();\n    var result = rect ?: new Rectangle();\n\t\n    with(rect) {\n        setWidth(5);\n        setHeight(10);\n    }\n\n    when {\n        side == Side.LEFT  -\u003e System.out.println(\"left  value\"),\n        side == Side.RIGHT -\u003e System.out.println(\"right value\")\n    }\n\t\n    repeat(3) {\n\tSystem.out.println(\"three time\");\n    }\n\t\n    int even = number.takeIf { it % 2 == 0 };\n    int odd  = number.takeUnless { it % 2 == 0 };\n```\n\nUsing this library developer can write in the following way.\n\n```Java\n   var rect = lazy(Rectangle::new);\n   var result = elvis(rect.get(), new Rectangle());\n   \n   with(rect, it -\u003e {\n       it.setWidth(5);\n       it.setHeight(10);\n   });\n   \n   when(\n       side == Side.LEFT,  () -\u003e System.out.println(\"left  value\"),\n       side == Side.RIGHT, () -\u003e System.out.println(\"right value\")\n   );\n   \n   repeat(3, () -\u003e {\n\t   System.out.println(\"three time\");\n   )\n   \n   int even = self(number).takeIf(it -\u003e it % 2 == 0);\n   int odd  = self(number).takeUnless(it -\u003e it % 2 == 0);\n```\n\nRequirements:\u003cbr/\u003e\nJava version: 8, 11\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklappdev%2Fjpml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklappdev%2Fjpml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklappdev%2Fjpml/lists"}