如何在 Java 中表示不相交的联合类型?

How do I represent disjoint union types in Java?

假设我在系统中有一个数据定义:

A PaymentMethod is one of Cash or CreditCard(String accountNum).

操作此(不相交联合)数据的一种方法是使用访问者模式:

interface IPaymentMethod {

    <R> R visit(IPaymentMethod.IVisitor<R> visitor);

    interface IVisitor<R> {

        R visitCash();

        R visitCreditCard(String accountNum);
    }
}

class Cash implements IPaymentMethod {

    <R> R visit(IPaymentMethod.IVisitor<R> visitor) {
        return visitor.visitCash();
    }
}

class CreditCard implements IPaymentMethod {

    String accountNum;

    // constructor here

    <R> R visit(IPaymentMethod.IVisitor<R> visitor) {
        return visitor.visitCreditCard(this.accountNum);
    }
}

除了实现和使用访问者的冗长之外,它对扩展过于开放:如果我希望我的图书馆的消费者产生 IPaymentMethods,我只希望 CashCreditCards 将被 returned。但是,他们可能 return 他们自己的实现,这没有任何意义。这里是否有另一种模式可以更好地表示此数据并可以保证我只处理 CashCreditCards? (当然,如果不是为了String accountNum,一个enum就好了。)

/**
 * A PaymentMethod is one of Cash or CreditCard(String accountNum).
 */
public abstract class PaymentMethod {

    private PaymentMethod() {}

    /**
     * Creates a Cash payment method.
     */
    public static PaymentMethod makeCash() {
        return new Cash();
    }

    /**
     * Creates a CreditCard payment method with an account number.
     */
    public static PaymentMethod makeCreditCard(String accountNum) {
        return new CreditCard(accountNum);
    }

    public abstract <R> R visit(IVisitor<R> visitor);

    public interface IVisitor<R> {

        R visitCash();

        R visitCreditCard(String accountNum);
    }

    private static class Cash extends PaymentMethod {

        @Override
        public <R> R visit(IVisitor<R> visitor) {
            return visitor.visitCash();
        }
    }

    private static class CreditCard extends PaymentMethod {

        private final String accountNum;

        private CreditCard(String accountNum) {
            this.accountNum = accountNum;
        }

        @Override
        public <R> R visit(IVisitor<R> visitor) {
            return visitor.visitCreditCard(this.accountNum);
        }
    }
}

此解决方案保留访问者模式来对 PaymentMethod 的每个子class 执行操作,但使用工厂模式来创建这些子class。私有 PaymentMethod 构造函数阻止 class 的外部扩展,从而关闭联合。可以放宽访问修饰符(即使用包私有构造函数)以仅允许在包内进行扩展并允许提取现金和信用卡 classes.

它很冗长,是的,但它似乎是 Java <17 的最佳解决方案(请参阅 David Conrad 关于密封 classes 的评论)。