Java的Boolean类-为什么不使用枚举?


11

在我看来,布尔类是作为枚举实现的理想候选者。

从源代码来看,大多数类都是静态方法,可以将其不变地移至枚举,其余的则变得像枚举一样简单。比较原始(删除注释和静态方法):

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

带有枚举版本:

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

有什么理由使Boolean不能成为枚举?

如果这是要覆盖equals()方法的Sun代码,则它缺少在比较两个对象的引用之前比较它们的值的非常根本的检查。这就是我认为equals()方法应为的方式:

   public boolean equals(Object obj) {

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

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }

4
您是否预见了布尔值的另一个不正确或错误的值?

1
@MichaelT枚举不需要具有两个以上的值。在Java中这是毫无意义的,因为它具有用于处理booleans(if)的专门语句,但是从概念/类型理论的角度来看,布尔值和枚举都是求和类型的实例,因此我想问一下为什么它们没有不能弥合他们之间的鸿沟。
Doval 2014年

1
注意:您似乎还错过了valueOf(String)(会与枚举的valueOf冲突)的实现及其背后的魔力getBoolean,从而使它Boolean.valueOf("yes")返回true而不是false。两者都是1.0规范的一部分,因此需要适当的向后兼容性。

8
@MichaelT FileNotFound当然!
Donal Fellows 2014年

Answers:


13

好吧,我想我可以从争论开始,直到JDK 1.5才将 Java枚举添加到Java编程语言中因此,在定义Boolean类的早期,这种解决方案甚至无法替代。

话虽这么说,Java的声誉是保持发行版之间的向后兼容性,所以即使我们今天认为您的解决方案是一个不错的选择,我们也无法做到这一点,而不必使用旧的Boolean来破坏数千行代码类。


3
您可能会发现java.lang.Boolean 的Java 1.0语言规范会有所帮助。该new Boolean("True")new Boolean("true")也可能会导致一些问题与假设枚举执行。

允许多个(不可变的)对象似乎是错误的,因此对API使用提供的布尔构造函数并不是一个好主意-正如API文档所述。
高地马克

语言规范没有解决此类问题,因为它没有指定类的实现。这是最佳实施规范的方法。
高地马克

13

当您将它们与Java的boolean的以前的功能进行比较时,有些事情是行不通的,并且以相当令人惊讶的方式行不通。

我们将忽略拳击,因为这是1.5的新增内容。假设,如果Sun希望,他们可以使enum Boolean行为就像在上执行拳击一样class Boolean

然而,与早期类的功能相比,还有其他令人惊讶的方式(对于编码人员而言),这会突然中断。

valueOf(String)问题

一个简单的例子是:

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

此代码的运行给出:

真正
真正
假
假
线程“主”中的异常java.lang.IllegalArgumentException:没有枚举常量BooleanStuff.MyBoolean.FaLsE
    在java.lang.Enum.valueOf(Enum.java:236)
    在BooleanStuff $ MyBoolean.valueOf(BooleanStuff.java:17)
    在BooleanStuff.main(BooleanStuff.java:13)

这里的问题是我无法通过任何不符合TRUE或不FALSE符合的内容valueOf(String)

没关系...我们将使用自己的方法覆盖它...

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

但是...这里有问题。您不能覆盖静态方法

因此,所有被绕过代码trueTrue或其他一些情况下混合时会报错了-和一个运行时异常相当壮观。

valueOf更有趣

还有一些其他方面效果不佳:

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

对于foo,我只是收到关于装箱一个已经装箱的值的警告。但是,bar的代码是语法错误:

错误:(7,24)Java:找不到适合valueOf(BooleanStuff.MyBoolean)的方法
    方法BooleanStuff.MyBoolean.valueOf(java.lang.String)不适用
      (无法通过方法调用转换将实际参数BooleanStuff.MyBoolean转换为java.lang.String)
    方法java.lang.Enum.valueOf(java.lang.Class,java.lang.String)不适用
      (由于实际和正式参数列表的长度不同,因此无法从参数实例化)

如果我们将语法错误强制转换为String类型:

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

我们返回运行时错误:

真正
线程“主”中的异常java.lang.IllegalArgumentException:没有枚举常量BooleanStuff.MyBoolean.false
    在java.lang.Enum.valueOf(Enum.java:236)
    在BooleanStuff $ MyBoolean.valueOf(BooleanStuff.java:11)
    在BooleanStuff.main(BooleanStuff.java:7)

为什么有人会写那个?Dunno ...但是它的代码曾经有效,将不再起作用。


不要误会我的意思,我真的很喜欢给定的不变对象只有一个副本的想法。枚举确实解决了这个问题。我亲自遇到了供应商代码,其中的供应商代码看起来像这样:

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

从来没有工作(不,我没有,因为不正确的状态是固定的其他地方,并确定这打破了以奇怪的方式,我真的没有时间去调试解决它)。如果这是一个枚举,则该代码将起作用。

但是,围绕枚举的语法的必要性(区分大小写-深入探究其背后的enumConstantDirectoryvalueOf,对于其他枚举而言需要以这种方式工作的运行时错误)以及静态方法的工作方式会导致很多事情中断,从而阻止它被替换为布尔值。


1
如果要进行设计,那么请从头开始学习一种新的语言,该语言应具有Java的工作原理(并且没有),布尔对象类型应像结构一样枚举……这完全不适合Java现在的工作方式。我敢肯定,有一些语言设计师会为此而努力。如果可以重新开始使用Java 8和接口中的默认方法之类的东西,我敢肯定,许多Java的缺点可以解决得更加干净-同时,我非常感谢能够接受一些Java 1.3代码并仍在1.8中进行编译-这就是我们现在的样子。

添加offrom方法以及适当的javadoc 并不是很难。
assylias 2014年

@assylias与其他大多数Java代码的约定是valueOf从1.0开始就存在Boolean.valueOf()。要么Enums不能将valueOf用作静态方法,要么Boolean需要一种与其使用的方法不同的方法。要么破坏约定或兼容性-并且不让 Boolean作为枚举也不会破坏。因此,选择相当简单。

“但是...这里有一个问题。您不能覆盖静态方法。” 您没有“覆盖”任何东西-该方法在超类中都不存在。问题是,该方法是为所有枚举自动定义的,您不能重新定义它。
user102008

“对于foo,我只是收到有关将已装箱的值装箱的警告。但是,bar的代码是语法错误:”这是不正确的比较。在中Boolean.valueOf(Boolean.valueOf("TRUE")),有两种不同的 valueOf方法:valueOf(String)valueOf(boolean)。语法错误是因为您忘记实现valueOf(boolean)in MyBoolean。然后有两个电话之间的autounboxing,这是在对语言的硬编码Boolean,但没有MyBoolean。如果你执行valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())的作品
user102008

2

最有可能的原因是原始boolean类型不是Enum,并且装箱版本的原始类型的行为几乎与未装箱版本的行为相同。例如

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

(性能可能不尽相同,但这是不同的主题。)

如果您能写的话,那会有些奇怪:

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

但不是:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  

1
您可能希望显示if语句也不适用于枚举。

@MichaelT我想编译器仍然可以将其拆箱并if像当前一样进行工作。在另一方面,有没有办法忽略您已经添加额外的功能,这样的事实,Booleanboolean没有。
2014年

哇...您无法在Java中为布尔值编写switch语句?太疯狂了。
Thomas Eding

由于取消装箱,因此装箱类仅像图元一样起作用。整数没有+运算符。
高地马克

@HighlandMark是的,但是我的意思是,他们竭尽全力确保装箱的类型以与原始类型相同的方式可用。拆箱是他们必须实施的操作,它不是免费提供的。
2014年

0

除了valueOf问题(这是Java级别的问题,它在JVM级别上可以正常工作)之外,这还因为Boolean它具有公共构造函数。这是一个坏主意,目前已被弃用,但这是可以保留的主意。


0

原因是“布尔”早于“枚举”是Java语言的一部分。多年来,非常希望拥有“布尔”,而没有“枚举”。只是现在您可以说“如果从一开始就可以使用枚举,那么我们可以将bool作为枚举而不是单独的类型来实现”。

在Swift中,可以将“ bool”表示为枚举,其中有三个结构分别实现了“ ExpressibleByBooleanLiteral”协议的“ Bool”,“ DarwinBoolean”和“ ObjCBool​​”结构。(DarwinBoolean与C或C ++ bool兼容,ObjCBool​​与Objective-C BOOL兼容)。“ true”和“ false”是编译器可识别的特殊值,并且只能用于初始化支持“ ExpressibleByBooleanLiteral”协议的对象。布尔有一个内部变量“ _value”,其中包含一位整数。

因此,布尔不是Swift语言的一部分,而是标准库的一部分。true和false是语言的一部分,ExpressibleByBooleanLiteral协议也是如此。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.