2

之前已经问过我遇到的问题:How to implement an interface with an enum, where the interface extends Comparable?

但是,没有一个解决方案可以解决我的确切问题,即:

我有一个值对象,类似于BigDecimal. 有时该值不会用真实对象设置,因为该值尚不知道。所以我想用空对象模式来表示这个对象没有定义的时间。这一切都不是问题,直到我尝试让我的 Null 对象实现Comparable接口。这是一个 SSCCE 来说明:

public class ComparableEnumHarness {
  public static interface Foo extends Comparable<Foo> {
    int getValue();
  }

  public static class VerySimpleFoo implements Foo {
    private final int value;

    public VerySimpleFoo(int value) {
      this.value = value;
    }

    @Override
    public int compareTo(Foo f) {
      return Integer.valueOf(value).compareTo(f.getValue());
    }

    @Override
    public int getValue() {
      return value;
    }
  }

  // Error is in the following line:
  // The interface Comparable cannot be implemented more than once with different arguments:
  // Comparable<ComparableEnumHarness.NullFoo> and Comparable<ComparableEnumHarness.Foo>
  public static enum NullFoo implements Foo {
    INSTANCE;

    @Override
    public int compareTo(Foo f) {
      return f == this ? 0 : -1; // NullFoo is less than everything except itself
    }

    @Override
    public int getValue() {
      return Integer.MIN_VALUE;
    }
  }
}

其他担忧:

  • 在实际示例中,我在Foo这里调用的内容有多个子类。
  • 我可能可以通过NullFoo不是一个来解决这个问题enum,但是我不能保证它只有一个实例,即Effective Java Item 3, pg。17-18
4

3 回答 3

2

我不推荐 NullObject 模式,因为我总是发现自己处于以下两种情况之一:

  • 像对象一样使用 NullObject 是没有意义的,它应该保留null
  • NullObject 具有太多的意义,不能只是一个 NullObject,它本身应该是一个真正的对象(例如,当它像一个功能齐全的默认值一样时)

根据我们在评论中的讨论,在我看来,您的 NullObject 的行为非常类似于您的普通对象的 0 值。

我要做的实际上是使用 0 (或任何更有意义的默认值),如果您真的需要知道它是否已被初始化,请放置一个标志。这样,您将需要考虑两件事:

  • 所有未初始化的值都不会与我的解决方案共享同一个实例
  • 出于同样的原因,您现在可以稍后初始化您的对象,而无需创建新实例

这是我想到的那种代码:

public static class VerySimpleFoo implements Foo {
    private int value;
    private boolean initialized;

    public VerySimpleFoo() {
      this.value = 0; // whatever default value makes more sense
      this.initialized = false;
    }

    public VerySimpleFoo(int value) {
      this.value = value;
      this.initialized = true;
    }

    @Override
    public int compareTo(Foo f) {
      // possibly need some distinction here, depending on your default value
      // and the behavior you expect
      return Integer.valueOf(value).compareTo(f.getValue());
    }

    @Override
    public int getValue() {
      return value;
    }

    public void setValue(int value) {
      this.value = value;
      this.initialized = true;
    }

    public boolean isInitialized() {
      return initialized;
    }
}
于 2014-04-23T15:11:48.760 回答
1

正如您所建议的,我相信一种解决方案是使用类而不是枚举:

public class NullFoo implements Foo {

    private NullFoo() {
    }

    public static final Foo INSTANCE = new NullFoo();

    @Override
    public int compareTo(Foo f) {
        return f == this ? 0 : -1;
    }

    @Override
    public int getValue() {
        return 0;
    }

}

这模仿了枚举行为,但它允许您实现 Foo 接口。由于私有构造函数,该类不可实例化,因此唯一可用的实例是可访问的实例 via NullFoo.INSTANCE,它是线程安全的(感谢final修饰符)。

于 2014-04-23T15:06:14.943 回答
0

问题是 Enum 已经在本机实现了 Comparable ,并且由于泛型只是一个糖代码,并且在编译后丢失,因此实际上您希望为同一个接口实现两次相同的方法。

对于 NullFoo,我会删除枚举,将其转换为类(如您建议的那样),并使用私有构造函数进行最终的公共静态实例引用,(这不如使用枚举好,但在大多数情况下是可以接受的)。

于 2014-04-23T14:19:22.393 回答