Java中联合类型的实现

开发代码时,有时某个对象有时需要包含一种类型的值或另一种类型的值。 支持联合概念的编程语言允许在某一时刻将当前值保存在一个存储区中。

例如,在C / C ++中,您可以这样编写。

union value { int i; float f; }; union value v; vi = 5; /* vf - undefined behaivor */ 

此外,如果将值设置为一个字段,则读取另一字段的值将具有未定义的行为。

为了简化在C ++ 17中使用联合类型的工作,添加了std :: variant类。

  std::variant<int, float> v { 5 }; std::cout << "int value: " << std::get<int>(v) << std::endl; 

Java语言不支持联合类型。 作为替代方案,您可以使用带有setter和getter的某些类型的两个字段来实现数据类。 但我希望将值存储在一个字段中,而不要存储在两个字段中。

如您所知,对象类型可以保存一种类型的值,然后重新分配另一种类型的值。 这可以用来实现一个类,例如类std :: variant。

由于在Java中无法在泛型中指定可变数量的类型,因此对于一定数量的类型,您需要对类进行特殊化处理(Union2,Union3等)。 让我们编写主类Union及其基本操作。

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

联合类的具体专业化将存储一定数量的类型和一个字段Object。 如果我们指出了错误的类型,我们将得到一个错误。

  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

Source: https://habr.com/ru/post/zh-CN465097/


All Articles