تطبيق مطابقة النمط في Java

تدعم العديد من اللغات الحديثة مطابقة الأنماط على مستوى اللغة. لا تدعم Java حاليًا مطابقة الأنماط ، ولكن هناك أمل في أن تتغير الأشياء في المستقبل.


المقارنة مع العينة تكشف للمطور القدرة على كتابة التعليمات البرمجية بشكل أكثر مرونة وجمالا ، مع تركها مفهومة.


باستخدام إمكانيات Java 8 ، يمكنك تطبيق بعض ميزات مطابقة الأنماط في شكل مكتبة. في هذه الحالة ، يمكنك استخدام كل من العبارة والتعبير.


نمط ثابت يسمح التحقق من المساواة مع الثوابت. في Java ، يسمح لك المحول بالتحقق من وجود أرقام متساوية وتعدادات وسلاسل. لكن في بعض الأحيان أريد التحقق من مساواة ثوابت الكائنات باستخدام طريقة يساوي ().


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); }; 

بالنسبة لخيارات التفرع المختلفة ، تحتاج إلى استخدام الأحمال الزائدة لطريقة التطابق (). لذلك ، في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بستة ثوابت.


 import org.kl.state.Else; import org.kl.state.Null; import static org.kl.pattern.ConstantPattern.matches; matches(data, 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) ); 

يتيح لك نمط Tuple التحقق من مساواة العديد من التغييرات مع الثوابت في وقت واحد.


 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 "); }; 

في الوقت الحالي ، تدعم المكتبة القدرة على تحديد 4 متغيرات و 6 فروع.


 import org.kl.state.Else; import org.kl.state.Null; import static org.kl.pattern.TuplePattern.matches; matches(side, width, "top", 25, () -> System.out.println("top"); "bottom", 30, () -> System.out.println("bottom"); "left", 15, () -> System.out.println("left"); "right", 15, () -> System.out.println("right"); Null.class, () -> System.out.println("Null value"), Else.class, () -> System.out.println("Default value") ); 

يسمح لك نمط اختبار الكتابة بالمطابقة في نفس الوقت مع النوع واستخراج قيمة المتغير. في Java ، لهذا نحتاج أولاً إلى التحقق من الكتابة ، والضغط على النوع ثم تعيينه لمتغير جديد.


 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); }; 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بستة أنواع.


 import org.kl.state.Else; import org.kl.state.Null; import static org.kl.pattern.VerifyPattern.matches; matches(data, 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); } ); 

يتيح لك نمط الحماية إمكانية مطابقة النوع في وقت واحد والتحقق من الظروف.


 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); }; 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير مع 6 أنواع وشروط.


 import org.kl.state.Else; import org.kl.state.Null; import static org.kl.pattern.GuardPattern.matches; matches(data, 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); } ); 

لتبسيط كتابة الشرط ، يمكن للمطور استخدام الوظائف التالية للمقارنة: أقل / لتر ، أكبرثان / gt ، أقلثانأورق / لو ، أكبرثانأوركوال / ge ،
يساوي / مكافئ ، notEqual / ne. ومن أجل حذف الشروط ، يمكنك تجربة: دائمًا / نعم ، أبدًا / لا.


 matches(data, 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); } ); 

يتيح لك نمط التفكيك مطابقة النوع وتحلل الكائن إلى مكونات في وقت واحد. في Java ، لهذا نحتاج أولاً إلى التحقق من الكتابة ، والاطلاع على نوع ما ، وتعيينه لمتغير جديد ، وعندئذٍ فقط الوصول إلى حقول الفصل الدراسي عبر الحروف.


 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)); } 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بثلاثة أنواع وتحلل الكائن إلى 3 مكونات.


 import org.kl.state.Else; import static org.kl.pattern.DeconstructPattern.matches; import static org.kl.pattern.DeconstructPattern.foreach; import static org.kl.pattern.DeconstructPattern.let; Figure figure = new Rectangle(); let(figure, (int w, int h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure, 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)); }); 

علاوة على ذلك ، من أجل الحصول على مكون ، يجب أن يكون للفصل طريقة أو أكثر من طرق التفكيك. يجب تسمية هذه الطرق باستخدام " استخراج التعليقات التوضيحية".
يجب أن تكون جميع المعلمات مفتوحة. نظرًا لأنه لا يمكن نقل العناصر الأولية إلى طريقة بالرجوع إليها ، فأنت بحاجة إلى استخدام أدوات الالتفاف للأدوات الأولية IntRef و FloatRef ، إلخ.


 import org.kl.type.IntRef; ... @Extract public void deconstruct(IntRef width, IntRef height) { width.set(this.width); height.set(this.height); } 

باستخدام Java 11 أيضًا ، يمكنك استنتاج أنواع من معلمات تفكيك.


 Figure figure = new Rectangle(); let(figure, (var w, var h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure, Rectangle.class, (var w, var h) -> out.println("square: " + (w * h)), Circle.class, (var r) -> out.println("square: " + (2 * Math.PI * r)), Else.class, () -> out.println("Default square: " + 0) ); foreach(listRectangles, (var w, var h) -> { System.out.println("square: " + (w * h)); }); 

يتيح لك نمط الخاصية مطابقة النوع والوصول إلى حقول الفصل بأسمائهم في وقت واحد. بدلاً من توفير جميع الحقول ، يمكنك الوصول إلى الحقول التي تحتاج إليها بأي ترتيب.


 let (w: int w, h:int h) = figure; switch (figure) { case Rect(w: int w == 5, h: int h == 10) -> out.println("sqr: " + (w * h)); case Rect(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)); } 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بثلاثة أنواع وتحلل الكائن إلى 3 مكونات.


 import org.kl.state.Else; import static org.kl.pattern.PropertyPattern.matches; import static org.kl.pattern.PropertyPattern.foreach; import static org.kl.pattern.PropertyPattern.let; import static org.kl.pattern.PropertyPattern.of; Figure figure = new Rectangle(); let(figure, of("w", "h"), (int w, int h) -> { System.out.println("border: " + w + " " + h)); }); matches(figure, 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, Rect.class, Rect::w, Rect::h, (int w, int h) -> out.println("sqr: " + (w * h)), Circle.class, Circle::r, (int r) -> 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)); }); 

يتيح لك نمط الموضع إمكانية مطابقة النوع في وقت واحد والتحقق من قيمة الحقول في ترتيب الإعلان. في Java ، لهذا نحتاج أولاً إلى التحقق من النوع ، وإلقائه على نوع ، وتعيينه لمتغير جديد ، وعندئذٍ فقط الوصول إلى حقول الفصل عبر getters والتحقق من المساواة.


 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); }; 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بستة أنواع والتحقق من 4 حقول في آن واحد.


 import org.kl.state.Else; import org.kl.state.Null; import static org.kl.pattern.PositionPattern.matches; import static org.kl.pattern.PositionPattern.of; matches(data, 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); } ); 

أيضًا ، إذا لم يرغب المطور في التحقق من بعض الحقول ، فيجب وضع علامة على هذه الحقول بتعليقات توضيحية @. يجب الإعلان عن هذه الحقول أخيرًا.


 class Circle { private int radius; @Exclude private int temp; } 

يسمح لك النمط الثابت بمطابقة النوع وفك الكائن في وقت واحد باستخدام أساليب المصنع.


 Optional some = ...; switch (some) { case Optional.empty() -> System.out.println("empty value"); case Optional.of(var v) -> System.out.println("value: " + v); default -> System.out.println("Default value"); }; 

في الوقت الحالي ، تدعم المكتبة القدرة على التحقق من قيم المتغير بستة أنواع وتقسيم الكائن إلى 3 مكونات.


 import org.kl.state.Else; import static org.kl.pattern.StaticPattern.matches; import static org.kl.pattern.StaticPattern.of; Optional some = ...; matches(figure, Optinal.class, of("empty"), () -> System.out.println("empty value"), Optinal.class, of("of") , (var v) -> System.out.println("value: " + v), Else.class, () -> System.out.println("Default value") ); 

في الوقت نفسه ، للحصول على المكون ، يجب أن يكون لدى الفصل طريقة أو أكثر من طرق التفكيك ، وهي عبارة عن استخراج التعليقات التوضيحية.


 @Extract public void of(IntRef value) { value.set(this.value); } 

يسهل نمط التسلسل معالجة تسلسل البيانات.


 List<Integer> list = ...; switch (list) { case Ranges.head(var h) -> System.out.println("list head: " + h); case Ranges.tail(var t) -> System.out.println("list tail: " + t); case Ranges.empty() -> System.out.println("Empty value"); default -> System.out.println("Default value"); }; 

باستخدام المكتبة ، يمكنك الكتابة على النحو التالي.


 import org.kl.state.Else; import org.kl.range.Ranges; import static org.kl.pattern.SequencePattern.matches; List<Integer> list = ...; matches(figure, Ranges.head(), (var h) -> System.out.println("list head: " + h), Ranges.tail(), (var t) -> System.out.println("list tail: " + t), Ranges.empty() () -> System.out.println("Empty value"), Else.class, () -> System.out.println("Default value") ); 

أيضا ، لتبسيط التعليمات البرمجية ، يمكنك استخدام الوظائف التالية.


 import static org.kl.pattern.CommonPattern.with; import static org.kl.pattern.CommonPattern.when; Rectangle rect = 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") ); 

كما ترون مطابقة الأنماط هي أداة قوية تجعل شفرة الكتابة أسهل بكثير. باستخدام تعبيرات lambda ، ومراجع الطريقة واشتقاق أنواع معلمات lambda ، يمكنك محاكاة إمكانيات مطابقة الأنماط باستخدام وسائل اللغة ذاتها.


شفرة المصدر للمكتبة مفتوحة ومتاحة على جيثب .

Source: https://habr.com/ru/post/ar460773/


All Articles