2

The problem I'm having has already been asked before: How to implement an interface with an enum, where the interface extends Comparable?

However, none of the solutions solve my exact problem, which is this:

I have a value object, similar to BigDecimal. Sometimes this value will not be set with a real object, because that value is not yet known. So I want to use the Null Object Pattern to represent the times this object is not defined. This is all not a problem, until I try to make my Null Object implement the Comparable interface. Here's an SSCCE to illustrate:

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;
    }
  }
}

Other concerns:

  • In the real example, there are multiple subclasses of what I'm calling Foo here.
  • I could probably work around this by having NullFoo not be an enum, but then I can't guarantee there is ever only exactly one instance of it, i.e. Effective Java Item 3, pg. 17-18
Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • I'm not fond of the NullObject Pattern. Especially because it leads you to this kind of stuff. Does it make sense in your program to compare non-initialized objects represented by your NullObject? – Joffrey Apr 23 '14 at 14:08
  • @Joffrey It's not "undefined", it's semantically "Zero". Although thinking of it that way may be the right way to solve this problem... ["Once a problem is described in sufficient detail, its solution is obvious"](http://forums.philosophyforums.com/threads/rubber-ducking-52910.html) – durron597 Apr 23 '14 at 14:54
  • *I want to use the Null Object Pattern to represent the times this object is not defined.* -- Well, I thought you meant **undefined** there. But anyway, `Zero` is not `null`, nor `NullObject`. What is the type of your class? What is it supposed to represent when it's not "null"? You said *similar to BigDecimal*, so I assume it's numbers? – Joffrey Apr 23 '14 at 14:57
  • @Joffrey It's a progressively updated value, but it starts at something which zero is a reasonable initial value. **undefined** is not the right word, **uninitialized** is better. – durron597 Apr 23 '14 at 15:01
  • Well you brought up *undefined*, I had in fact used *non-initialized* myself ^^ So my point is, does it make sense to actually use your uninitialized objects in comparisons? If you want it to be semantically 0, and to behave as such, then why not use a 0 value of your normal object? You could have a flag saying that it is not initialized if you need it later. – Joffrey Apr 23 '14 at 15:03

3 Answers3

2

I don't recommend the NullObject pattern because I always find myself in one of these 2 situations:

  • it does not make sense to use NullObject like an object, and it should stay null
  • NullObject has too much meaning to be just a NullObject, and should be a true object itself (for instance, when it acts like a fully functional default value)

According to our discussion in the comments, it seems to me that your NullObject behaves very much like the 0 value of your normal objects.

What I would do is actually use 0 (or whatever default value makes more sense), and put a flag if you really need to know whether it has been initialized. This way, you will have 2 things to consider:

  • all uninitialized values won't share the same instance with my solution
  • for the very same reason, you are now able to initialize your object later without having to create a new instance

Here is the kind of code I think of:

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;
    }
}
Joffrey
  • 32,348
  • 6
  • 68
  • 100
  • There will be an unexpected behavior when comparing a non-initialized instance with a negative-value instance. The compareTo method needs to be improved. – sp00m Apr 23 '14 at 15:14
  • @sp00m Thanks for pointing this out. In that case the OP can modify `compareTo()` easily to deal with this ;) I'll edit my post if necessary. – Joffrey Apr 23 '14 at 15:15
  • @sp00m In the SSCCE yes, in reality a negative value instance can never exist. In fact a zero value instance will never exist except in the NullObject case – durron597 Apr 23 '14 at 15:15
  • @durron597 Alright, then Joffrey's answer will work as expected ;) – sp00m Apr 23 '14 at 15:17
1

As you suggested, I believe one solution would be to use a class instead of an enum:

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;
    }

}

This mimics an enum behavior, but it allows you to implement your Foo interface. The class is not instantiable because of the private constructor, so the only instance available is the one accessible via NullFoo.INSTANCE, which is thread-safe (thanks to the final modifier).

sp00m
  • 47,968
  • 31
  • 142
  • 252
  • FYI I changed my SSCCE to have `getValue` be `Integer.MIN_VALUE`, I was lazy in that section of writing the SSCCE but for the purposes of the `compareTo` that makes more sense in the SSCCE version. I guess it wasn't very "correct" :) – durron597 Apr 23 '14 at 15:17
0

The thing is that Enum already implements Comparable natively, and since the generics are just a sugar code, and lost after compilation, effectively you want to implement the same method twice for the same interface.

I would drop enum, for NullFoo, converting it to class (like you suggested), and make final public static INSTANCE reference with private constructor, (This is not as good as using an enum, but acceptable, in most cases).

mavarazy
  • 7,562
  • 1
  • 34
  • 60