首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java中模拟ADTs

在Java中模拟ADTs
EN

Stack Overflow用户
提问于 2015-07-15 11:14:43
回答 3查看 446关注 0票数 9

一个应用程序可以以两种模式运行--“实时”模式,它查看世界状态的每一次更新,或者“采样”模式,其中它只查看世界的状态--每T毫秒一次。

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

代码语言:javascript
复制
data Mode = RealTime | Sampled Int

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

代码语言:javascript
复制
case mode of
    RealTime         -> -- do realtime stuff
    Sampled interval -> -- do sample stuff with 'interval'

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

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

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

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-15 11:51:53

在像Java这样的语言中模拟封闭的代数数据类型的传统方法是访客模式,它只提供开放的类(可以在任何时候继承)。

代码语言:javascript
复制
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();
    }
}

正如@ over 3237465所指出的,数据类型的几个编码是可能的,当数据类型不是递归的时候,这些编码恰好是一致的: Church编码是折叠的:它允许您通过教堂编码的数据类型递归积累一个值。Scott编码对应于实际的模式匹配。在任何情况下,访问者都可以用来实现所有这些编码。谢谢你的催促,@ nudge 3237465!

票数 9
EN

Stack Overflow用户

发布于 2015-07-15 11:23:58

您可以声明一个接口Mode

代码语言:javascript
复制
public interface Mode {
    void doSmth();
}

然后可以有两个类实现该接口。

代码语言:javascript
复制
public class Realtime implements Mode {
    @Override
    public void doSmth() {
        // Do realtime stuff
    }
}

public class Sampled implements Mode {
    private final int interval;

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

    @Override
    public void doSmth() {
        // Do sampled stuff
    }
}

然后声明一个类型为Mode的变量

代码语言:javascript
复制
final Mode mode = ... // Initialize however you want, calling one of the constructors.

然后,由于modeMode类型的,所以只能访问接口中声明的内容。因此,您只需调用mode.doSmth(),根据初始化mode的方式,它将实时运行或采样。

如果这些类共享一些代码,那么您可以声明一个abstract类而不是一个interface,并且有两个类extend该类。因此,他们可以调用在abstract类中声明的常用方法(应该使用protected可见性来声明这些方法,这样只有子类才能看到它们)。

票数 2
EN

Stack Overflow用户

发布于 2015-07-15 11:39:41

是的,它是有可能的,尽管它取决于你准备达到的复杂程度。

如果您想达到最高级别的类型安全性,我建议您声明一个抽象(名为AbstractModeManager),其职责是查询世界状态并将其存储在实例变量中,这些变量应该可以通过公共getter方法访问。它还必须知道何时有最新情况:

代码语言:javascript
复制
abstract class AbstractModeManager
{
    // Instance variables to contain the last state read.
    private ... state;

    protected void queryTheStateOfTheWorld(...) throws ...
    {
        ... fill this.state ...
    }

    public ... getState()
    {
        return this.state;
    }

    public abstract void updateOccurred(...);
}

在这种情况下,您必须编写两个实现:一个用于实时实现,另一个用于采样:

代码语言:javascript
复制
class RealTimeModeManager extends AbstractModeManager
{
    public void updateOccurred(...)
    {
        // In this implementation, every time there has been an update, 
        // a query must be performed:
        queryTheStateOfTheWorld(...);
    }
}

class SampledModeManager extends AbstractModeManager
{
    public SampledModeManager(int interval)
    {
        ... program a timer to call the queryTheStateOfTheWorld ...
    }

    public void updateOccurred(...)
    {
        // Do nothing.
    }
}

最后,您必须决定在每种情况下必须使用哪一种,并将其存储在一个变量中:

代码语言:javascript
复制
private AbstractModeManager mode=...

..。并在系统中的每次更新中系统地调用它:

代码语言:javascript
复制
mode.updateOccurred(...)

这样,在初始化该变量之后,您将不会关心系统运行的是哪种模式:这种行为将留给您的抽象处理。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31428870

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档