عند تطوير التعليمات البرمجية ، من الضروري في بعض الأحيان أن يحتوي الكائن على قيم من نوع أو قيم من نوع آخر في مرحلة ما. تسمح لغات البرمجة التي تدعم مفهوم النقابات في لحظة معينة بحفظ القيمة الحالية في منطقة ذاكرة واحدة.
على سبيل المثال ، في C / C ++ ، يمكنك كتابة مثل هذا.
union value { int i; float f; }; union value v; vi = 5;
علاوة على ذلك ، إذا قمنا بتعيين القيمة إلى حقل واحد ، فإن قراءة قيمة حقل آخر سيكون لها سلوك غير محدد.
لتبسيط العمل مع أنواع الاتحاد في C ++ 17 ، تمت إضافة الفئة std :: variant.
std::variant<int, float> v { 5 }; std::cout << "int value: " << std::get<int>(v) << std::endl;
لغة Java لا تدعم أنواع الاتحاد. وكبديل لذلك ، يمكنك تطبيق فئة بيانات مع حقلين من أنواع معينة باستخدام أدوات التثبيت والحروف. لكنني أردت تخزين القيمة في حقل واحد وليس في حقلين.
كما تعلمون ، يمكن لنوع الكائن حفظ قيمة نوع واحد ، ثم إعادة تعيين قيم نوع آخر. ويمكن استخدام هذا لتنفيذ فئة ، مثل الفئة std :: variant.
نظرًا لأنه في Java لا يمكنك تحديد عدد متغير من الأنواع بشكل عام ، وبالنسبة لعدد معين من الأنواع التي تحتاجها لتخصص فئة (Union2 ، Union3 ، إلخ). دعنا نكتب اتحاد الطبقة الرئيسية وعملياته الأساسية.
public abstract class Union { private Union() {} public abstract <T> void set(T value); public abstract <T> T get(Class<T> clazz); public abstract <T> boolean isActive(Class<T> clazz); public abstract <T> Class<T> getActive(); }
لإنشاء كائنات فئة ، سنستخدم أساليب المصنع. بناءً على عدد الأنواع ، سيتم إرجاع التخصص المحدد للفصل الدراسي.
public static <T1, T2> Union2<T1, T2> of(Class<T1> firstClass, Class<T2> secondClass) { return new Union2<>(firstClass, secondClass); } public static <T1, T2, T3> Union3<T1, T2, T3> of(Class<T1> firstClass, Class<T2> secondClass, Class<T3> thirdClass) { return new Union3<>(firstClass, secondClass, thirdClass); }
سيحدد التخصص الملموس في فئة الاتحاد عددًا معينًا من الأنواع وحقل كائن واحد. إذا أشرنا إلى نوع غير صحيح ، فسنحصل على خطأ.
private static class Union2<T1, T2> extends Union { private final Class<T1> firstClass; private final Class<T2> secondClass; private Object value; private Union2(Class<T1> firstClass, Class<T2> secondClass) { this.firstClass = firstClass; this.secondClass = secondClass; } @Override public <T> void set(T value) { if (value.getClass() == firstClass || value.getClass() == secondClass) { this.value = value; } else { throw new UnionException("Incorrect type: " + value.getClass().getName() + "\n" + "Union two types: [" + firstClass.getName() + ", " + secondClass.getName() + "]"); } } @Override public <T> T get(Class<T> clazz) { if (clazz == firstClass || clazz == secondClass) { return (T) value; } else { throw new UnionException("Incorrect type: " + value.getClass().getName() + "\n" + "Union two types: [" + firstClass.getName() + ", " + secondClass.getName() + "]"); } } @Override public <T> boolean isActive(Class<T> clazz) { return value.getClass() == clazz; } @Override public <T> Class<T> getActive() { return (Class<T>) value.getClass(); } } private static class Union3<T1, T2, T3> extends Union { private final Class<T1> firstClass; private final Class<T2> secondClass; private final Class<T3> thirdClass; private Object value; private Union3(Class<T1> firstClass, Class<T2> secondClass, Class<T3> thirdClass) { this.firstClass = firstClass; this.secondClass = secondClass; this.thirdClass = thirdClass; } @Override public <T> void set(T value) { if (value.getClass() == firstClass || value.getClass() == secondClass || value.getClass() == thirdClass) { this.value = value; } else { throw new UnionException("Incorrect type: " + value.getClass().getName() + "\n" + "Union three types: [" + firstClass.getName() + ", " + secondClass.getName() + ", " + thirdClass.getName() + "]"); } } @Override public <T> T get(Class<T> clazz) { if (clazz == firstClass || clazz == secondClass || value.getClass() == thirdClass) { return (T) value; } else { throw new UnionException("Incorrect type: " + value.getClass().getName() + "\n" + "Union three types: [" + firstClass.getName() + ", " + secondClass.getName() + ", " + thirdClass.getName() + "]"); } } @Override public <T> boolean isActive(Class<T> clazz) { return value.getClass() == clazz; } @Override public <T> Class<T> getActive() { return (Class<T>) value.getClass(); } }
الآن دعونا نلقي نظرة على مثال عن كيفية استخدام هذه الفئة. كما ترى ، لا يعمل Union مع تخصصات محددة ، مما يجعل الكود أكثر بساطة.
Union triUnion = Union.of(Integer.class, String.class, Float.class); triUnion.set(15f); assertEquals(triUnion.getActive(), Float.class); assertTrue(triUnion.isActive(Float.class)); triUnion.set("Dot"); assertEquals(triUnion.getActive(), String.class); assertTrue(triUnion.isActive(String.class)); triUnion.set(10); assertEquals(triUnion.getActive(), Integer.class); assertTrue(triUnion.isActive(Integer.class));
يمكنك أيضًا كتابة زائر بسيط للتحقق من القيمة الحالية.
Union biUnion = Union.of(Integer.class, String.class); biUnion.set("Line"); Union triUnion = Union.of(Integer.class, String.class, Float.class); triUnion.set(15f); matches(biUnion, Integer.class, i -> System.out.println("bi-union number: " + i), String.class, s -> System.out.println("bi-union string: " + s) ); matches(triUnion, Integer.class, i -> System.out.println("tri-union int: " + i), String.class, s -> System.out.println("tri-union string: " + s), Float.class, f -> System.out.println("tri-union float: " + f) );
public static <V, T1, T2> void matches(V value, Class<T1> firstClazz, Consumer<T1> firstConsumer, Class<T2> secondClazz, Consumer<T2> secondConsumer) { Class<?> valueClass = value.getClass(); if (firstClazz == valueClass) { firstConsumer.accept((T1) value); } else if (secondClazz == valueClass) { secondConsumer.accept((T2) value); } } public static <T1, T2, T3> void matches(Union value, Class<T1> firstClazz, Purchaser<T1> firstConsumer, Class<T2> secondClazz, Purchaser<T2> secondConsumer, Class<T3> thirdClazz, Purchaser<T3> thirdConsumer) { Class<?> valueClass = value.getActive(); if (firstClazz == valueClass) { firstConsumer.obtain(value.get(firstClazz)); } else if (secondClazz == valueClass) { secondConsumer.obtain(value.get(secondClazz)); } else if (thirdClazz == valueClass) { thirdConsumer.obtain(value.get(thirdClazz)); } }
بإيجاز ، يمكننا القول أنه في لغة Java ، يمكن تنفيذ دعم أنواع الاتحادات على مستوى المكتبة. ولكن كعيب ، في كل عدد من الأنواع ، تحتاج إلى تخصصك الخاص في فئة الاتحاد بالإضافة إلى حفظ جميع الأنواع.
يمكن الاطلاع على شفرة المصدر الكاملة للفئة على github:
code