用Java模拟ADT

时间:2015-07-15 11:14:44

标签: java haskell algebraic-data-types

应用程序可以以两种模式运行 - "实时"它在哪里查看世界状况的每次更新,或者"采样"它只在每T毫秒观察世界状态。

如果我正在编写Haskell(或任何使用ADT的语言),我会将其建模为

data Mode = RealTime | Sampled Int

可以以类型安全的方式使用如下

case mode of
    RealTime         -> -- do realtime stuff
    Sampled interval -> -- do sample stuff with 'interval'

我说这是"类型安全"因为如果您在实时模式下运行,则无法尝试访问interval字段(如果您在采样模式下操作,则会在您需要时提供该字段)。

如何以类型安全的方式在Java中对相同的东西进行建模?也就是说,我想要

  • 定义区分两种模式的类(或枚举),以及
  • 禁止在实时模式下访问interval字段,
  • 让所有这些都由编译器检查。

这在Java中可行吗?如果没有,那么实现这种类型安全的惯用方法是什么?

1 个答案:

答案 0 :(得分:9)

在Java这样的语言中模拟封闭代数数据类型的传统方法是Visitor pattern

只能以类型安全的方式提供开放类(可以随时继承)
abstract class Mode {
    public abstract <T> T accept(ModeVisitor<T> visitor);
}

final class RealTime extends Mode {
    public RealTime() {}

    public <T> T accept(ModeVisitor<T> visitor) {
        return visitor.visit(this);
    }
}

final class Sampled extends Mode {
    private final int interval;

    public Sampled(int interval) {
        this.interval = interval;
    }

    public int getInterval() {
        return this.interval;
    }

    public <T> T accept(ModeVisitor<T> visitor) {
        return visitor.visit(this);
    }
}

// The recursion principle itself
abstract class ModeVisitor<T> {
    public abstract T visit(RealTime mode);
    public abstract T visit(Sampled mode);
}

// Concrete uses of the recursion principle
final class ModeShow extends ModeVisitor<String> {
    private ModeShow() {}

    public static String show(Mode mode) {
        return mode.accept(new ModeShow());
    }

    public String visit(RealTime mode) {
        return "RealTime";
    }

    public String visit(Sampled mode) {
        return "Sampled " + mode.getInterval();
    }
}

正如@ user3237465所说,数据类型的几种编码是可能的,当数据类型不是递归时,发生重合:教会编码是一个折叠:它允许你积累通过Church编码的数据类型递归的值。 Scott编码对应于实际模式匹配。无论如何,访问者可以用来实现所有这些编码。感谢nudge,@ user3237465!