11

It seems to me that the Boolean class is an ideal candidate to be implemented as an enum.

Looking at the source code, most of the class is static methods which could be moved unchanged to an enum, the rest become much simpler as an enum. Compare original (comments and static methods removed):

public final class Boolean implements java.io.Serializable,
                                      Comparable<Boolean>
{
   public static final Boolean TRUE = new Boolean(true);
  public static final Boolean FALSE = new Boolean(false);
   private final boolean value;
   public Boolean(boolean value) {
       this.value = value;
   }
   public Boolean(String s) {
       this(toBoolean(s));
   }
   public boolean booleanValue() {
       return value;
   }
   public String toString() {
       return value ? "true" : "false";
   }
   public int hashCode() {
       return value ? 1231 : 1237;
   }
   public boolean equals(Object obj) {
       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
   public int compareTo(Boolean b) {
       return compare(this.value, b.value);
   }
}

with an enum version:

public enum Boolean implements Comparable<Boolean>
{
   FALSE(false), TRUE(true);
   private Boolean(boolean value) {
       this.value = value;
   }
   private final boolean value;
   public boolean booleanValue() {
       return value;
   }

   public String toString() {
       return value ? "true" : "false";
   }
}

Is there any reason why Boolean couldn't become an enum?

If this is the Sun code to override the equals() method, it is missing a very fundamental check of comparing the references of the two objects before comparing their values. This is how I think the equals() method should be:

   public boolean equals(Object obj) {

       if (this == obj) {
          return true;
       }

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
4
  • 4
    Do you foresee another value for a boolean that isn't true or false?
    – user40980
    Mar 28, 2014 at 20:05
  • 1
    @MichaelT An enum doesn't need to have more than 2 values. It'd be kind of pointless in Java because it has a specialized statement for processing booleans (if) but from a conceptual/type theory point of view booleans and enums are both instances of sum types, so I think it's fair to ask why they didn't bridge the gap between them.
    – Doval
    Mar 28, 2014 at 20:29
  • 1
    Note: You also appear to have missed out on the implementation of valueOf(String) (which would conflict with the enum's valueOf) and the magic behind getBoolean which can make it so that Boolean.valueOf("yes") returns true rather than false. Both of which are part of the 1.0 spec and would need appropriate backwards compatibility.
    – user40980
    Mar 28, 2014 at 20:35
  • 8
    @MichaelT FileNotFound of course! Mar 28, 2014 at 21:56

5 Answers 5

13

Well, I guess I could start by arguing that Java enumerations were not added to the Java programming language until the JDK 1.5. and therefore this solution was not even an alternative in the early days when the Boolean class was defined.

That being said, Java has the reputation of keeping backwards compatibility between releases and so, even if we, today, may consider your solution as a good alternative, we cannot do it without breaking thousands of lines of code out there already using the old Boolean class.

3
  • 3
    You might find the Java 1.0 language spec for java.lang.Boolean to be of help. The new Boolean("True") and new Boolean("true") may also cause some problems with the hypothetical enum implementation.
    – user40980
    Mar 28, 2014 at 20:25
  • It seems wrong to allow multiple (immutable) objects, and so using the provided Constructors on Boolean is not a good idea - as the API documentation says. Mar 28, 2014 at 22:15
  • The language spec wouldn't help with this sort of question as it doesn't specify the implementations of the Classes. This is how best to implement the specification. Mar 28, 2014 at 22:26
13

There are some things that don't work, and don't work in rather surprising ways when you compare them to previous functionality of Java's boolean.

We're going to ignore boxing as that was something added with 1.5. Hypothetically, if Sun wanted to they could have made the enum Boolean to behave just like the boxing performed on the class Boolean.

Yet, there are other surprising (to the coder) ways that this would suddenly break compared to the functionality of the earlier class.

The valueOf(String) problem

A simple example of this is:

public class BooleanStuff {
    public static void main(String args[]) {
        Boolean foo = Boolean.valueOf("TRUE");
        System.out.println(foo);
        foo = Boolean.valueOf("TrUe");
        System.out.println(foo);
        foo = Boolean.valueOf("yes");  // this is actually false
        System.out.println(foo);

        // Above this line is perfectly acceptable Java 1.3
        // Below this line takes Java 1.5 or later

        MyBoolean bar;
        bar = MyBoolean.valueOf("FALSE");
        System.out.println(bar);
        bar = MyBoolean.valueOf("FaLsE");
        System.out.println(bar);
    }

    enum MyBoolean implements Comparable<MyBoolean> {
        FALSE(false), TRUE(true);
        private MyBoolean(boolean value) { this.value = value; }
        private final boolean value;
        public boolean booleanValue() { return value; }
        public String toString() { return value ? "true" : "false"; }
    }
}

The run of this code gives:

true
true
false
false
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant BooleanStuff.MyBoolean.FaLsE
    at java.lang.Enum.valueOf(Enum.java:236)
    at BooleanStuff$MyBoolean.valueOf(BooleanStuff.java:17)
    at BooleanStuff.main(BooleanStuff.java:13)

The problem here is that I can't pass through anything that isn't TRUE or FALSE to valueOf(String).

That's ok... we'll just override it with our own method...

    public static MyBoolean valueOf(String arg) {
        return arg.equalsIgnoreCase("true") ? TRUE : FALSE;
    }

But... there's a problem here. You can't override a static method.

And so, all the code that is passing around true or True or some other case mixed will error out - and quite spectacularly with a runtime exception.

Some more fun with valueOf

There are some other bits that don't work too well:

public static void main(String args[]) {
    Boolean foo = Boolean.valueOf(Boolean.valueOf("TRUE"));
    System.out.println(foo);

    MyBoolean bar = MyBoolean.valueOf(MyBoolean.valueOf("FALSE"));
    System.out.println(bar);
}

For foo, I just get a warning about boxing an already boxed value. However, the code for bar is a syntax error:

Error:(7, 24) java: no suitable method found for valueOf(BooleanStuff.MyBoolean)
    method BooleanStuff.MyBoolean.valueOf(java.lang.String) is not applicable
      (actual argument BooleanStuff.MyBoolean cannot be converted to java.lang.String by method invocation conversion)
    method java.lang.Enum.valueOf(java.lang.Class,java.lang.String) is not applicable
      (cannot instantiate from arguments because actual and formal argument lists differ in length)

If we coerce that syntax error back into a String type:

public static void main(String args[]) {
    Boolean foo = Boolean.valueOf(Boolean.valueOf("TRUE"));
    System.out.println(foo);

    MyBoolean bar = MyBoolean.valueOf(MyBoolean.valueOf("FALSE").toString());
    System.out.println(bar);
}

We get our runtime error back:

true
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant BooleanStuff.MyBoolean.false
    at java.lang.Enum.valueOf(Enum.java:236)
    at BooleanStuff$MyBoolean.valueOf(BooleanStuff.java:11)
    at BooleanStuff.main(BooleanStuff.java:7)

Why anyone would write that? Dunno... but its code that used to work and would no longer work.


Don't get me wrong, I really do like the idea of only one copy of a given immutable object ever. The enum does solve this problem. I've personally encountered vendor code that had bugs in it from vendor code that looked something like this:

if(boolValue == new Boolean("true")) { ... }

that never worked (No, I didn't fix it because the incorrect state was fixed somewhere else, and fixing this broke that in strange ways that I really didn't have the time to debug). If this was an enum, that code would have worked instead.

However, the necessities of the syntax around the enum (case sensitive - dig into the enumConstantDirectory behind valueOf, runtime errors that need to work that way for the other enums) and the way static methods work causes a number of things to break that prevents it from being a drop in replacement for a Boolean.

5
  • 1
    If one was designing, from scratch a new language with the knowledge of how Java works (and doesn't), having the Boolean object type be an enum like structure... it just doesn't quite fit with how Java works now. I'm sure there are some language designers kicking themselves for it. If one could start over with Java 8 and things like the default methods in interfaces, I'm sure that a lot of the misfeatures of Java could have been done quite a bit cleaner - at the same time I really do appreciate being able to take a some Java 1.3 code and still compile it in 1.8 - and thats where we are now.
    – user40980
    Mar 30, 2014 at 0:05
  • It wouldn't have been very difficult to add a of or from method and appropriate javadoc.
    – assylias
    Apr 1, 2014 at 23:03
  • @assylias the convention with much of the other java code is valueOf and Boolean.valueOf() has been there since 1.0. Either Enums wouldn't be able to use valueOf as a static method, or Boolean would need a different method than the one it has been using. Doing either breaks convention or compatibility - and not having Boolean be an enum breaks neither. From this, the choice is fairly simple.
    – user40980
    Apr 1, 2014 at 23:27
  • "But... there's a problem here. You can't override a static method." You are not "overriding" anything -- the method doesn't exist in a superclass anyway. The problem instead is that the method is automatically defined for all enums, and you cannot redefine it.
    – user102008
    Jul 9, 2015 at 23:38
  • "For foo, I just get a warning about boxing an already boxed value. However, the code for bar is a syntax error:" This is an incorrect comparison. In Boolean.valueOf(Boolean.valueOf("TRUE")), there are two different valueOf methods: valueOf(String) and valueOf(boolean). The syntax error is because you forgot to implement the valueOf(boolean) in MyBoolean. Then there's the autounboxing between the two calls, which is hardcoded in the language for Boolean but not MyBoolean.If you implemented valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue()) works
    – user102008
    Jul 9, 2015 at 23:47
2

Most likely because the primitive boolean type isn't an Enum, and the boxed versions of primitive types behave almost identically to their unboxed version. E.g.

Integer x = 5;
Integer y = 7;
Integer z = x + y;

(The performance may not be the same, but that's a different subject.)

It'd be kind of strange if you could write:

Boolean b = Boolean.TRUE;
switch (b) {
case Boolean.TRUE:
    // do things
    break;
case Boolean.FALSE:
    // do things
    break;
}

but not:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  
5
  • 1
    You might wish to show the if statement that wouldn't work with an enum either.
    – user40980
    Mar 28, 2014 at 20:20
  • @MichaelT I imagine the compiler would still be able to unbox it and make the if work just like it currently does. On the other hand there's no way to ignore the fact that you've added extra functionality to Boolean that boolean doesn't have.
    – Doval
    Mar 28, 2014 at 20:24
  • Whoa... you can't write switch statements for booleans in Java? That's crazy. Mar 28, 2014 at 21:58
  • The boxing classes only act like the primitives due to unboxing. Integer doesn't have a + operator. Mar 28, 2014 at 22:18
  • @HighlandMark That's true, but my point is that they went through great pains to make sure boxed types were usable in the same ways as their primitive counterparts. Unboxing is something they had to implement, it doesn't come for free.
    – Doval
    Mar 29, 2014 at 16:41
0

In addition to valueOf issue (which is an issue on Java level, it could work fine on JVM level), it's because Boolean has a public constructor. That was a bad idea, currently deprecated, but it's one that is here to stay.

0

The reason is that "bool" was part of the Java language much earlier than "enum". For many years, "bool" was very desirable to have, while "enum" wasn't available. Only now you can say "if enum had been available from the start, then we could have implemented bool as an enum instead of a separate type".

In Swift, which could have expressed "bool" as an enum, there are three structs named "Bool", "DarwinBoolean" and "ObjCBool" implementing the "ExpressibleByBooleanLiteral" protocol. (DarwinBoolean is compatible to a C or C++ bool, ObjCBool is compatible to an Objective-C BOOL). "true" and "false" are special values recognised by the compiler, and can only be used to initialise objects supporting the "ExpressibleByBooleanLiteral" protocol. Bool has an internal variable "_value" containing a one bit integer.

So Bool is not part of the Swift language, but of the standard library. true and false are part of the language, and so is the ExpressibleByBooleanLiteral protocol.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.