Bei der Entwicklung von Code muss ein Objekt manchmal Werte eines Typs oder irgendwann Werte eines anderen Typs enthalten. Programmiersprachen, die das Konzept der Gewerkschaften unterstützen, ermöglichen es zu einem bestimmten Zeitpunkt, den aktuellen Wert in einem Speicherbereich zu speichern.
In C / C ++ können Sie beispielsweise so schreiben.
union value { int i; float f; }; union value v; vi = 5;
Wenn wir den Wert auf ein Feld setzen, hat das Lesen des Werts eines anderen Feldes ein undefiniertes Verhalten.
Um die Arbeit mit Unionstypen in C ++ 17 zu vereinfachen, wurde die Klasse std :: variante hinzugefügt.
std::variant<int, float> v { 5 }; std::cout << "int value: " << std::get<int>(v) << std::endl;
Die Java-Sprache unterstützt keine Unionstypen. Alternativ können Sie eine Datenklasse mit zwei Feldern bestimmter Typen mit Setter und Getter implementieren. Aber ich wollte, dass der Wert in einem Feld und nicht in zwei Feldern gespeichert wird.
Wie Sie wissen, kann der Objekttyp den Wert eines Typs speichern und dann die Werte eines anderen Typs neu zuweisen. Und dies kann verwendet werden, um eine Klasse wie die Klasse std :: variante zu implementieren.
Da Sie in Java keine variable Anzahl von Typen in einem Generikum angeben können, müssen Sie für eine bestimmte Anzahl von Typen eine Klasse spezialisieren (Union2, Union3 usw.). Schreiben wir die Hauptklasse Union und ihre Grundoperationen.
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(); }
Um Klassenobjekte zu erstellen, verwenden wir Factory-Methoden. Abhängig von der Anzahl der Typen wird die spezifische Spezialisierung der Klasse zurückgegeben.
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); }
Die konkrete Spezialisierung der Union-Klasse speichert eine bestimmte Anzahl von Typen und ein Feld Objekt. Wenn wir einen falschen Typ angeben, wird eine Fehlermeldung angezeigt.
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(); } }
Schauen wir uns nun ein Beispiel für die Verwendung dieser Klasse an. Wie Sie sehen können, arbeitet Union nicht mit bestimmten Spezialisierungen, was den Code einfacher macht.
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));
Sie können auch einen einfachen Besucher schreiben, um den aktuellen Wert zu überprüfen.
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)); } }
Zusammenfassend können wir sagen, dass in der Java-Sprache die Unterstützung für Unionstypen auf Bibliotheksebene implementiert werden kann. Als Nachteil benötigen Sie jedoch für jede Anzahl von Typen eine eigene Spezialisierung der Gewerkschaftsklasse und speichern zusätzlich alle Typen.
Der vollständige Quellcode der Klasse kann auf github:
code angezeigt
werden