0

I have recently started to use Java after a long period of using C,C++ and C#. I can't get my head around how Java enums are supposed to work. After some research I have created the following:

public class RedRoad implements Serializable, Parcelable
{
 // ... other parts removed for clarity
  public enum State
  {
    NOT_STARTED(0),
    PART_DONE(1),
    COMPLETED(2);
    private int value;
    private State(int value) {
        this.value = value;
    }
    public void setValue(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
  }
}

I am storing these State values in an sqlite database as ints using getValue(), retrieving them using setValue(), then sending the resulting ''Road' objects via a broadcast. Then I am doing this:

    switch (road.state) {
                case COMPLETED:
                    pline.getOutlinePaint().setColor(Color.GREEN); break;
                case PART_DONE:
                    pline.getOutlinePaint().setColor(Color.argb(0xFF,0xFF,0xA5,0x00)); break;
                case NOT_STARTED:
                    default: pline.getOutlinePaint().setColor(Color.RED);
            }

but the first two cases are never called, even though I have checked that road.state.getValue() is sometimes 1 , not zero. Furthermore, if I change the switch code to this:

        switch (road.state.getValue()) {
                case 2:
                    pline.getOutlinePaint().setColor(Color.GREEN); break;
                case 1:
                    Log.d("*** road state ", String.valueOf(road.state.getValue()));
                    Log.e("*** road state ", String.valueOf(road.state));
                    pline.getOutlinePaint().setColor(Color.argb(0xFF,0xFF,0xA5,0x00));
                    break;
                case 0:
                default:
                    pline.getOutlinePaint().setColor(Color.RED);
            }

then the colour gets set as requested. And I get an extraordinary ouput in the log:

D/*** road state: 1
E/*** road state: NOT_STARTED

How can this be? NOT_STARTED is defined as zero!

[Edit] Later discovered that , apart from the above, (which I can find a workaround for), the values are incorrect after being sent from the async task to the main activity (the 'Road' class is parcelable). If I log state.getValue() before sending , then again after receipt, any non-zero values have changed to zero.

quilkin
  • 874
  • 11
  • 31
  • 1
    I notice you have setters in your enum. Enum values are supposed to be immutable; you should remove those. – Andy Turner Jan 12 '21 at 16:14
  • 1
    @AndyTurner depends on what you use them for I suppose. I've seen enums used as singleton objects storing values. It's not precisely wrong, but the 'one instance per JVM' thing does require caution – Jeroen Steenbeeke Jan 12 '21 at 16:19
  • @JeroenSteenbeeke the values are called [Enum Constants](https://docs.oracle.com/javase/specs/jls/se14/html/jls-8.html#jls-8.9) in the spec. They're intended to be constants. [More here](http://errorprone.info/bugpattern/ImmutableEnumChecker). – Andy Turner Jan 12 '21 at 16:21
  • 1
    @AndyTurner the spec also allows non-final members, so immutability is implied but not required. I'll agree that in almost all cases it's a bad idea to have mutable enums, for precisely the reasons in your second link. But we both know that we developers sometimes come up with unorthodox code (and the singleton example I gave is from the 2nd edition of Effective Java). That said: you're right that the setter in the question needs to be removed. – Jeroen Steenbeeke Jan 12 '21 at 16:30
  • @Andy Turner, OK, but then what's the correct way to store and retreive these values in a database? – quilkin Jan 12 '21 at 17:03

2 Answers2

0

You're calling String.valueOf on an enum constant in the second instance, which calls the toString method of the enum. The default toString implementation of enums returns the name field, which makes NOT_STARTED the valid return value.

Enums are essentially abstract classes with a finite number of implementations, with each implementation guaranteed to only exist once per JVM.

Jeroen Steenbeeke
  • 3,884
  • 5
  • 17
  • 26
  • Thanks, I understand your first paragraph and that may explain the odd log output. But I don't understand your second para, and I still don't know the correct way to achieve what I'm trying to do. – quilkin Jan 12 '21 at 17:06
  • I have added more info, does this help? – quilkin Jan 12 '21 at 21:04
-1

@AndyTurner is correct about not using a setter. I have now done some more tests, in a new app specially created, and the setter gives totally unpredictable results. So the solution I have used (for setting the value from an 'int' in the db) is a somewhat boring

protected void setState(int i) {
    switch (i)
    {
        case 2:     state = State.COMPLETED;     break;
        case 1:     state = State.PART_DONE;     break;
        default:    state = State.NOT_STARTED;
    }
}

Although this works , it seems to me to partly defeat the reason for using an Enum (instead of an int) to start with. And, since creating the setter (that I had originally) gives incorrect results, I wonder why Android Studio (4.1) didn't give me a warning about it? It's very happy to give me warnings about so many other (less important) things.

quilkin
  • 874
  • 11
  • 31
  • There's an easier way to convert from int to enum value: `State.values()[i]`. This will throw an `ArrayIndexOutOfBoundsException` on invalid values. To convert from enum to int use the builtin `ordinal()` method – Jeroen Steenbeeke Jan 13 '21 at 19:47
  • @JeroenSteenbeeke, OK, that maybe 'easier', but why didn't you suggest that yesterday? Then I wouldn't have needed to work out an alternative method. Also, is it the done thing to vote down an answer which _isn't incorrect_, although not the _best_ answer? – quilkin Jan 13 '21 at 22:17
  • 1
    I downvoted because your answer isn't an answer, but a continuation of your question, which should have been an edit of your original question. As for why I didn't suggest `values()` and `ordinal()` earlier: I understood your question to be "why am I seeing `NOT_STARTED` in my logs", which I answered. – Jeroen Steenbeeke Jan 14 '21 at 08:35