La classe booleana di Java - perché non un enum?

11

Mi sembra che la classe booleana sia un candidato ideale da implementare come enum.

Guardando il codice sorgente, la maggior parte della classe sono metodi statici che potrebbero essere spostati invariati a un enum, il resto diventa molto più semplice come enum. Confronta originale (commenti e metodi statici rimossi):

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

con una versione enum:

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

C'è qualche ragione per cui Boolean non potrebbe diventare un enum?

Se questo è il codice Sun per sovrascrivere il metodo equals (), manca un controllo molto fondamentale del confronto dei riferimenti dei due oggetti prima di confrontare i loro valori. Questo è il modo in cui penso che il metodo equals () dovrebbe essere:

   public boolean equals(Object obj) {

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

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
    
posta Highland Mark 28.03.2014 - 21:03
fonte

5 risposte

13

Bene, immagino di poter iniziare sostenendo che le enumerazioni Java non sono state aggiunte al linguaggio di programmazione Java fino a JDK 1.5. e quindi questa soluzione non era nemmeno un'alternativa nei primi giorni in cui era definita la classe booleana.

Detto questo, Java ha la reputazione di mantenere la retrocompatibilità tra le versioni e così, anche se noi, oggi, potremmo considerare la tua soluzione come una buona alternativa, non possiamo farlo senza infrangere migliaia di righe di codice che già utilizziamo la vecchia classe booleana.

    
risposta data 28.03.2014 - 21:16
fonte
13

Ci sono alcune cose che non funzionano e non funzionano in modo piuttosto sorprendente quando le si confronta con le precedenti funzionalità del booleano di Java.

Ignoreremo il pugilato in quanto è stato aggiunto qualcosa con 1.5. Ipoteticamente, se Sun avesse voluto, avrebbero potuto fare in modo che enum Boolean si comportasse esattamente come il boxing eseguito su class Boolean .

Tuttavia, esistono altri modi sorprendenti (per il programmatore) che questo si interromperebbe improvvisamente rispetto alla funzionalità della classe precedente.

Il problema valueOf (String)

Un semplice esempio di questo è:

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

L'esecuzione di questo codice fornisce:

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)

Il problema qui è che non posso passare attraverso tutto ciò che non è TRUE o FALSE a valueOf(String) .

Va bene ... lo sostituiremo con il nostro metodo ...

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

Ma ... c'è un problema qui. non può eseguire l'override di un metodo statico .

E così, tutto il codice che sta passando attorno a true o True oa qualche altro caso misto uscirà in errore - e in modo abbastanza spettacolare con un'eccezione di runtime.

Ancora più divertimento con valueOf

Ci sono altri bit che non funzionano molto bene:

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

Per foo , ricevo solo un avvertimento sulla boxing di un valore già in scatola. Tuttavia, il codice per la barra è un errore di sintassi:

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)

Se riusciamo a forzare l'errore di sintassi in un tipo 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);
}

Otteniamo il nostro errore di runtime:

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)

Perché qualcuno lo scriverebbe? Boh ... ma il suo codice funzionava e non funzionava più.

Non fraintendermi, mi piace davvero l'idea di una sola copia di un dato oggetto immutabile di sempre. L'enum risolve questo problema. Ho incontrato personalmente il codice del fornitore che conteneva bug nel codice del fornitore che assomigliava a questo:

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

che mai ha lavorato (No, non l'ho risolto perché lo stato errato era stato riparato da qualche altra parte, e aggiustandolo si è rotto in modo strano che davvero non l'ho fatto t avere il tempo di eseguire il debug) . Se questo fosse un enum, quel codice avrebbe funzionato invece.

Tuttavia, le necessità della sintassi attorno all'enumerazione (sensibile alla distinzione tra maiuscole e minuscole - scavare nel enumConstantDirectory dietro valueOf , errori di runtime che devono funzionare in questo modo per le altre enumerazioni) e il modo in cui i metodi statici funzionano un numero di cose da interrompere che impedisce che si verifichi una sostituzione in sostituzione di un booleano.

    
risposta data 29.03.2014 - 20:30
fonte
2

Molto probabilmente perché il tipo primitivo boolean non è un Enum , e le versioni in scatola dei tipi primitivi si comportano in modo quasi identico alla loro versione non condivisa. Per es.

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

(La performance potrebbe non essere la stessa, ma è un argomento diverso.)

Sarebbe strano se tu potessi scrivere:

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

ma non:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  
    
risposta data 28.03.2014 - 21:18
fonte
0

Oltre al problema di valueOf (che è un problema a livello di Java, potrebbe funzionare bene a livello di JVM), perché Boolean ha un costruttore pubblico. Questa era una cattiva idea, attualmente deprecata, ma è una cosa che è qui per rimanere.

    
risposta data 06.04.2018 - 15:03
fonte
0

Il motivo è che "bool" faceva parte del linguaggio Java molto prima di "enum". Per molti anni, "bool" era molto desiderabile, mentre "enum" non era disponibile. Solo ora puoi dire "se enum fosse stato disponibile fin dall'inizio, avremmo potuto implementare bool come enum invece di un tipo separato".

In Swift, che avrebbe potuto esprimere "bool" come enum, ci sono tre strutture denominate "Bool", "DarwinBoolean" e "ObjCBool" che implementano il protocollo "ExpressibleByBooleanLiteral". (DarwinBoolean è compatibile con un booster C o C ++, ObjCBool è compatibile con un BOOL Objective-C). "true" e "false" sono valori speciali riconosciuti dal compilatore e possono essere utilizzati solo per inizializzare gli oggetti che supportano il protocollo "ExpressibleByBooleanLiteral". Bool ha una variabile interna "_value" contenente un intero di un bit.

Quindi Bool non fa parte della lingua Swift, ma della libreria standard. true e false fanno parte del linguaggio, così come il protocollo ExpressibleByBooleanLiteral.

    
risposta data 07.04.2018 - 14:03
fonte

Leggi altre domande sui tag