有没有办法在 Java 中定义 sum 类型? Java 似乎天生就直接支持乘积类型,我认为枚举可能允许它支持 sum 类型,而继承看起来也许可以做到,但至少有一种情况我无法解决。 详细地说,sum 类型是一种可以恰好具有一组不同类型中的一个的类型,例如 C 中的标记联合。 就我而言,我正在尝试在 Java 中实现 haskell 的 Either 类型:
data Either a b = Left a | Right b
但在基础级别,我必须将其实现为产品类型,而忽略其中一个字段:
public class Either<L,R>
{
private L left = null;
private R right = null;
public static <L,R> Either<L,R> right(R right)
{
return new Either<>(null, right);
}
public static <L,R> Either<L,R> left(L left)
{
return new Either<>(left, null);
}
private Either(L left, R right) throws IllegalArgumentException
{
this.left = left;
this.right = right;
if (left != null && right != null)
{
throw new IllegalArgumentException("An Either cannot be created with two values");
}
if (left == right)
{
throw new IllegalArgumentException("An Either cannot be created without a value");
}
}
.
.
.
}
我尝试通过继承来实现这一点,但我必须使用通配符类型参数或等效参数,这是 Java 泛型不允许的:
public class Left<L> extends Either<L,?>
我没有太多使用 Java 的 Enums,但是虽然它们似乎是次佳候选,但我并不抱希望。
在这一点上,我认为这可能只能通过类型转换 Object
值来实现,我希望完全避免这种情况,除非有一种方法可以安全地做到这一点,并且能够使用它适用于所有总和类型。
最佳答案
制作 Either
一个没有字段且只有一个构造函数(私有(private)、无参数、空)的抽象类,并将您的“数据构造函数”(left
和 right
静态工厂方法)嵌套在类中,以便他们可以看到私有(private)构造函数,但没有别的可以,有效地密封类型。
使用抽象方法 either
模拟详尽的模式匹配,在静态工厂方法返回的具体类型中适本地覆盖。根据 fromLeft
实现便利方法(如 fromRight
、 bimap
、 first
、 second
、 either
) .
import java.util.Optional;
import java.util.function.Function;
public abstract class Either<A, B> {
private Either() {}
public abstract <C> C either(Function<? super A, ? extends C> left,
Function<? super B, ? extends C> right);
public static <A, B> Either<A, B> left(A value) {
return new Either<A, B>() {
@Override
public <C> C either(Function<? super A, ? extends C> left,
Function<? super B, ? extends C> right) {
return left.apply(value);
}
};
}
public static <A, B> Either<A, B> right(B value) {
return new Either<A, B>() {
@Override
public <C> C either(Function<? super A, ? extends C> left,
Function<? super B, ? extends C> right) {
return right.apply(value);
}
};
}
public Optional<A> fromLeft() {
return this.either(Optional::of, value -> Optional.empty());
}
}
愉快又安全!没办法搞砸。因为类型是有效密封的,所以您可以放心,只有两种情况,并且每个操作最终都必须根据 either
定义。方法,它强制调用者处理这两种情况。
关于您尝试解决的问题 class Left<L> extends Either<L,?>
,考虑签名<A, B> Either<A, B> left(A value)
.类型参数B
不会出现在参数列表中。因此,给定某个类型的值 A
,您可以获得Either<A, B>
对于任何类型B
.
关于Java 标记的 union/sum 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48143268/