Saat mengembangkan kode, terkadang diperlukan suatu objek untuk memuat nilai dari satu jenis atau nilai dari jenis lain di beberapa titik. Bahasa pemrograman yang mendukung konsep serikat memungkinkan pada saat tertentu untuk menyimpan nilai saat ini dalam satu area memori.
Misalnya, dalam C / C ++ Anda dapat menulis seperti ini.
union value { int i; float f; }; union value v; vi = 5;
Selain itu, jika kita menetapkan nilai ke satu bidang, maka membaca nilai bidang lain akan memiliki perilaku yang tidak terdefinisi.
Untuk menyederhanakan bekerja dengan jenis serikat di C ++ 17, kelas std :: varian ditambahkan.
std::variant<int, float> v { 5 }; std::cout << "int value: " << std::get<int>(v) << std::endl;
Bahasa Jawa tidak mendukung jenis penyatuan. Sebagai alternatif, Anda bisa mengimplementasikan kelas data dengan dua bidang tipe tertentu dengan setter dan getter. Tapi saya ingin nilai disimpan dalam satu bidang, dan bukan dalam dua.
Seperti yang Anda ketahui, tipe objek dapat menyimpan nilai dari satu tipe, dan kemudian menetapkan kembali nilai-nilai dari tipe lain. Dan ini dapat digunakan untuk mengimplementasikan kelas, seperti kelas std :: varian.
Karena di Jawa Anda tidak dapat menentukan jumlah variabel tipe dalam generik, untuk sejumlah jenis tertentu Anda memerlukan spesialisasi kelas (Union2, Union3, dll.). Mari kita tulis kelas utama Union dan operasi dasarnya.
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(); }
Untuk membuat objek kelas, kita akan menggunakan metode pabrik. Bergantung pada jumlah jenis, spesialisasi spesifik kelas akan dikembalikan.
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); }
Spesialisasi konkret dari kelas serikat akan menyimpan sejumlah jenis dan satu objek lapangan. Jika kami menunjukkan jenis yang salah, kami akan mendapatkan kesalahan.
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(); } }
Sekarang mari kita lihat contoh bagaimana menggunakan kelas ini. Seperti yang Anda lihat, Union tidak bekerja dengan spesialisasi khusus, yang membuat kode lebih sederhana.
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));
Anda juga dapat menulis pengunjung sederhana untuk memeriksa nilai saat ini.
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)); } }
Kesimpulannya, kita dapat mengatakan bahwa dalam bahasa Jawa, dukungan untuk jenis-jenis serikat dapat diimplementasikan di tingkat perpustakaan. Tetapi sebagai kekurangan, untuk setiap jumlah jenis Anda membutuhkan spesialisasi Anda sendiri dari kelas serikat pekerja dan juga menyimpan semua jenis.
Kode sumber lengkap kelas dapat dilihat pada
kode github :.