Javas Boolesche Klasse - warum nicht eine Aufzählung?

11

Es scheint mir, dass die Boolesche Klasse ein idealer Kandidat ist, um als Aufzählung implementiert zu werden.

Mit Blick auf den Quellcode besteht der größte Teil der Klasse aus statischen Methoden, die unverändert in eine Aufzählung verschoben werden können. Der Rest wird als Aufzählung viel einfacher. Original vergleichen (Kommentare und statische Methoden entfernt):

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

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

Gibt es einen Grund, warum Boolean keine Aufzählung werden konnte?

Wenn dies der Sun-Code zum Überschreiben der equals () -Methode ist, fehlt eine sehr grundlegende Überprüfung des Vergleichs der Referenzen der beiden Objekte vor dem Vergleich ihrer Werte. So sollte meiner Meinung nach die equals () -Methode sein:

   public boolean equals(Object obj) {

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

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
Highland Mark
quelle
4
Sehen Sie einen anderen Wert für einen Booleschen Wert vor, der nicht wahr oder falsch ist?
1
@MichaelT Eine Aufzählung muss nicht mehr als 2 Werte haben. In Java wäre es irgendwie sinnlos, weil es eine spezielle Anweisung für die Verarbeitung von Booleschen Werten ( if) gibt, aber aus konzeptioneller / typentheoretischer Sicht sind Boolesche Werte und Aufzählungen beide Beispiele für Summentypen. Ich denke, es ist fair zu fragen, warum sie dies nicht getan haben Überbrücken Sie nicht die Lücke zwischen ihnen.
Doval
1
Hinweis: Sie scheinen auch die Implementierung von valueOf(String)(was mit dem Wert der Aufzählung in Konflikt stehen würde) und die Magie, die dahinter steckt, verpasst zu haben, getBooleansodass Boolean.valueOf("yes")true und nicht false zurückgegeben wird. Beide sind Teil der 1.0-Spezifikation und erfordern eine angemessene Abwärtskompatibilität.
8
@ MichaelT FileNotFound natürlich!
Donal Fellows

Antworten:

13

Nun, ich denke, ich könnte damit beginnen, zu argumentieren, dass der Java-Programmiersprache erst mit JDK 1.5 Java-Aufzählungen hinzugefügt wurden . und deshalb war diese Lösung in den frühen Tagen, als die Boolesche Klasse definiert wurde, nicht einmal eine Alternative.

Abgesehen davon hat Java den Ruf, die Abwärtskompatibilität zwischen Releases aufrechtzuerhalten. Selbst wenn wir Ihre Lösung heute als gute Alternative betrachten, können wir dies nicht tun, ohne Tausende von Codezeilen zu unterbrechen, die bereits den alten Booleschen Wert verwenden Klasse.

edalorzo
quelle
3
Möglicherweise ist die Java 1.0- Sprachspezifikation für java.lang.Boolean hilfreich. Das new Boolean("True")und new Boolean("true")kann auch einige Probleme mit der hypothetischen Enum-Implementierung verursachen.
Es scheint falsch, mehrere (unveränderliche) Objekte zuzulassen. Daher ist die Verwendung der bereitgestellten Konstruktoren auf Boolean keine gute Idee - wie in der API-Dokumentation angegeben.
Highland Mark
Die Sprachspezifikation würde bei dieser Art von Fragen nicht helfen, da sie die Implementierungen der Klassen nicht spezifiziert. So lässt sich die Spezifikation am besten implementieren.
Highland Mark
13

Es gibt einige Dinge, die nicht funktionieren und nicht auf überraschende Weise funktionieren, wenn Sie sie mit früheren Funktionen von Javas Booleschem Wert vergleichen.

Wir werden das Boxen ignorieren, da dies mit 1.5 hinzugefügt wurde. Wenn Sun wollte, hätten sie das hypothetisch dazu bringen können, enum Booleansich genauso zu verhalten wie das Boxen, das am class Boolean.

Es gibt jedoch andere überraschende (für den Codierer) Möglichkeiten, wie dies im Vergleich zur Funktionalität der früheren Klasse plötzlich brechen würde.

Das Problem valueOf (String)

Ein einfaches Beispiel hierfür ist:

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

Der Lauf dieses Codes ergibt:

wahr
wahr
falsch
falsch
Ausnahme im Thread "main" java.lang.IllegalArgumentException: Keine Enum-Konstante BooleanStuff.MyBoolean.FaLsE
    at java.lang.Enum.valueOf (Enum.java:236)
    bei BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    bei BooleanStuff.main (BooleanStuff.java:13)

Das Problem hier ist , dass ich nichts passieren kann , der nicht ist TRUEoder FALSEzu valueOf(String).

Das ist in Ordnung ... wir überschreiben es einfach mit unserer eigenen Methode ...

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

Aber ... hier gibt es ein Problem. Sie können eine statische Methode nicht überschreiben .

Und so, die alle Code, um vergehen trueoder Trueoder ein anderer Fall gemischt werden Fehler aus - und ganz spektakulär mit einer Laufzeit Ausnahme.

Noch mehr Spaß mit valueOf

Es gibt einige andere Teile, die nicht so gut funktionieren:

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

Denn fooich bekomme nur eine Warnung über das Boxen eines bereits verpackten Wertes. Der Code für bar ist jedoch ein Syntaxfehler:

Fehler: (7, 24) Java: Keine geeignete Methode für valueOf gefunden (BooleanStuff.MyBoolean)
    Die Methode BooleanStuff.MyBoolean.valueOf (java.lang.String) ist nicht anwendbar
      (Das eigentliche Argument BooleanStuff.MyBoolean kann nicht durch Konvertierung des Methodenaufrufs in java.lang.String konvertiert werden.)
    Die Methode java.lang.Enum.valueOf (java.lang.Class, java.lang.String) ist nicht anwendbar
      (Kann nicht anhand von Argumenten instanziiert werden, da die tatsächlichen und formalen Argumentlisten unterschiedlich lang sind.)

Wenn wir diesen Syntaxfehler zurück in einen StringTyp zwingen :

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

Wir bekommen unseren Laufzeitfehler zurück:

wahr
Ausnahme im Thread "main" java.lang.IllegalArgumentException: Keine Enum-Konstante BooleanStuff.MyBoolean.false
    at java.lang.Enum.valueOf (Enum.java:236)
    bei BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    bei BooleanStuff.main (BooleanStuff.java:7)

Warum sollte jemand das schreiben? Keine Ahnung ... aber sein Code, der früher funktionierte und nicht mehr funktionierte.


Versteh mich nicht falsch, ich mag wirklich die Idee, immer nur eine Kopie eines bestimmten unveränderlichen Objekts zu haben. Die Aufzählung löst dieses Problem. Ich bin persönlich auf Herstellercode gestoßen, der Fehler im Herstellercode enthielt, der ungefähr so ​​aussah:

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

das hat nie funktioniert (Nein, ich habe es nicht behoben, weil der falsche Status woanders behoben wurde, und das Beheben dieses Problems hat das auf seltsame Weise gebrochen, dass ich wirklich keine Zeit zum Debuggen hatte) . Wenn dies eine Aufzählung wäre, hätte dieser Code stattdessen funktioniert.

Die Notwendigkeit der Syntax rund um die Aufzählung (Groß- und Kleinschreibung beachten - in das dahinter stehende enumConstantDirectory eintauchenvalueOf , Laufzeitfehler, die für die anderen Aufzählungen auf diese Weise funktionieren müssen) und die Art und Weise, wie statische Methoden funktionieren, führen jedoch dazu, dass eine Reihe von Dingen beschädigt werden, die dies verhindern ein Tropfen auf den Ersatz für einen Booleschen.

Gemeinschaft
quelle
1
Wenn man eine neue Sprache von Grund auf neu entwarf und wusste, wie Java funktioniert (und nicht), sollte der boolesche Objekttyp eine enumähnliche Struktur sein ... es passt einfach nicht ganz zur heutigen Funktionsweise von Java. Ich bin mir sicher, dass es einige Sprachdesigner gibt, die sich dafür strampeln. Wenn man mit Java 8 und Dingen wie den Standardmethoden in Schnittstellen von vorne anfangen könnte, wäre ich sicher, dass viele der Fehlfunktionen von Java ein bisschen sauberer hätten gemacht werden können - gleichzeitig schätze ich es wirklich, in der Lage zu sein einen Java 1.3-Code und kompilieren Sie ihn immer noch in 1.8 - und dort sind wir jetzt.
Es wäre nicht sehr schwierig gewesen, eine ofoder fromMethode und ein geeignetes Javadoc hinzuzufügen .
Assylias
@assylias die Konvention mit einem Großteil des anderen Java-Codes ist valueOfund Boolean.valueOf () ist seit 1.0 vorhanden . Entweder kann Enums valueOf nicht als statische Methode verwenden, oder Boolean benötigt eine andere Methode als die bisher verwendete. Entweder Pausen Konvention oder Kompatibilität zu tun - und nicht mit Booleschen weder eine ENUM - Pausen sein. Daher ist die Auswahl ziemlich einfach.
"Aber ... hier gibt es ein Problem. Sie können eine statische Methode nicht überschreiben." Sie "überschreiben" nichts - die Methode existiert sowieso nicht in einer Oberklasse. Das Problem ist stattdessen, dass die Methode automatisch für alle Aufzählungen definiert wird und Sie sie nicht neu definieren können.
user102008
"Für foo erhalte ich nur eine Warnung über das Einpacken eines bereits eingepackten Werts. Der Code für bar ist jedoch ein Syntaxfehler:" Dies ist ein falscher Vergleich. In Boolean.valueOf(Boolean.valueOf("TRUE"))gibt es zwei verschiedene valueOf Methoden: valueOf(String)und valueOf(boolean). Der Syntaxfehler liegt daran, dass Sie vergessen haben, das valueOf(boolean)in zu implementieren MyBoolean. Dann gibt es das Autounboxing zwischen den beiden Aufrufen, das in der Sprache für Booleanaber nicht fest MyBooleancodiert ist. Wenn Sie valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())Werke implementiert haben
user102008
2

Höchstwahrscheinlich, weil der primitive booleanTyp kein Enumist und sich die Box-Versionen der primitiven Typen fast identisch mit ihrer Unbox-Version verhalten. Z.B

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

(Die Leistung ist möglicherweise nicht dieselbe, aber das ist ein anderes Thema.)

Es wäre seltsam, wenn Sie schreiben könnten:

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

aber nicht:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  
Doval
quelle
1
Möglicherweise möchten Sie die if-Anweisung anzeigen, die auch mit einer Aufzählung nicht funktioniert.
@MichaelT Ich kann mir vorstellen, dass der Compiler es immer noch entpacken und die ifArbeit so machen kann, wie es derzeit der Fall ist. Auf der anderen Seite gibt es keine Möglichkeit, die Tatsache zu ignorieren, dass Sie zusätzliche Funktionen hinzugefügt haben, Booleandie booleandies nicht hat.
Doval
Whoa ... Sie können in Java keine switch-Anweisungen für Boolesche Werte schreiben? Das ist verrückt.
Thomas Eding
Die Boxklassen verhalten sich aufgrund des Unboxing nur wie die Grundelemente. Integer hat keinen + Operator.
Highland Mark
@HighlandMark Das stimmt, aber mein Punkt ist, dass sie große Anstrengungen unternommen haben, um sicherzustellen, dass Box-Typen auf die gleiche Weise wie ihre primitiven Gegenstücke verwendet werden können. Unboxing mussten sie implementieren, es ist nicht kostenlos.
Doval
0

Zusätzlich zum valueOfProblem (das ein Problem auf Java-Ebene ist, könnte es auf JVM-Ebene gut funktionieren) liegt es daran, dass Booleanes einen öffentlichen Konstruktor hat. Das war eine schlechte Idee, die derzeit veraltet ist, aber sie ist hier, um zu bleiben.

Konrad Borowski
quelle
0

Der Grund ist, dass "bool" viel früher als "enum" Teil der Java-Sprache war. Für viele Jahre war "bool" sehr wünschenswert, während "enum" nicht verfügbar war. Erst jetzt können Sie sagen: "Wenn Enum von Anfang an verfügbar gewesen wäre, hätten wir Bool als Enum anstelle eines separaten Typs implementieren können."

In Swift, das "bool" als Aufzählung hätte ausdrücken können, gibt es drei Strukturen mit den Namen "Bool", "DarwinBoolean" und "ObjCBool", die das Protokoll "ExpressibleByBooleanLiteral" implementieren. (DarwinBoolean ist kompatibel mit einem C- oder C ++ - Bool, ObjCBool ​​ist kompatibel mit einem Objective-C-BOOL). "true" und "false" sind vom Compiler erkannte Sonderwerte und können nur zum Initialisieren von Objekten verwendet werden, die das Protokoll "ExpressibleByBooleanLiteral" unterstützen. Bool hat eine interne Variable "_value", die eine 1-Bit-Ganzzahl enthält.

Bool ist also nicht Teil der Swift-Sprache, sondern der Standardbibliothek. true und false sind Teil der Sprache, ebenso wie das ExpressibleByBooleanLiteral-Protokoll.

gnasher729
quelle