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


11

Mi sembra che la classe booleana sia la candidata ideale per essere implementata come enum.

Guardando il codice sorgente, la maggior parte della classe sono metodi statici che potrebbero essere spostati invariati in 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 motivo per cui Boolean non potrebbe diventare un enum?

Se questo è il codice Sun per sovrascrivere il metodo equals (), manca un controllo fondamentale per confrontare i riferimenti dei due oggetti prima di confrontare i loro valori. Ecco come 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;
   }

4
Prevedi un altro valore per un valore booleano che non è vero o falso?

1
@MichaelT Un enum non deve avere più di 2 valori. Sarebbe un po 'inutile in Java perché ha un'istruzione specializzata per l'elaborazione di booleani ( if) ma da un punto di vista della teoria concettuale / tipo i booleani e gli enum sono entrambi esempi di tipi di somma, quindi penso che sia giusto chiedersi perché non lo abbiano fatto colmare il divario tra di loro.
Doval,

1
Nota: sembra anche che tu abbia perso l'implementazione di valueOf(String)(che sarebbe in conflitto con il valore di enum di Of) e la magia dietro la getBooleanquale può farlo in modo che Boolean.valueOf("yes")ritorni vero piuttosto che falso. Entrambi fanno parte delle specifiche 1.0 e richiederebbero un'adeguata compatibilità con le versioni precedenti.

8
@MichaelT FileNotFound ovviamente!
Donal Fellows,

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 stata definita la classe booleana.

Detto questo, Java ha la reputazione di mantenere la retrocompatibilità tra le versioni e quindi, anche se oggi, potremmo considerare la tua soluzione come una buona alternativa, non possiamo farcela senza spezzare migliaia di righe di codice che già usano il vecchio Booleano classe.


3
Potresti trovare utili le specifiche del linguaggio Java 1.0 per java.lang.Boolean. Il new Boolean("True")e new Boolean("true")può anche causare alcuni problemi con l'ipotetica implementazione dell'enum.

Sembra sbagliato consentire più oggetti (immutabili), e quindi usare i costruttori forniti su Boolean non è una buona idea - come dice la documentazione dell'API.
Highland Mark,

Le specifiche del linguaggio non aiuterebbero con questo tipo di domanda in quanto non specificano le implementazioni delle Classi. Questo è il modo migliore per implementare la specifica.
Highland Mark,

13

Ci sono alcune cose che non funzionano e non funzionano in modi piuttosto sorprendenti quando le confronti con le funzionalità precedenti del booleano Java.

Ignoreremo la boxe poiché è stato aggiunto qualcosa con 1.5. Ipoteticamente, se Sun lo avesse voluto, avrebbero potuto enum Booleancomportarsi come il pugilato eseguito sul class Boolean.

Tuttavia, ci sono altri modi sorprendenti (per il programmatore) che questo si spezzerebbe 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:

vero
vero
falso
falso
Eccezione nel thread "main" java.lang.IllegalArgumentException: nessuna costante enum BooleanStuff.MyBoolean.FaLsE
    at java.lang.Enum.valueOf (Enum.java:236)
    su BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    su BooleanStuff.main (BooleanStuff.java:13)

Il problema qui è che non riesco a passare attraverso tutto ciò che non è TRUEo FALSEa valueOf(String).

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

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

Ma ... c'è un problema qui. Non è possibile ignorare un metodo statico .

E così, tutto il codice che sta passando in giro trueo Trueo qualche altro caso misto si guasterà - e in modo abbastanza spettacolare con un'eccezione di runtime.

Un po 'più di divertimento con valueOf

Ci sono alcuni altri bit che non funzionano troppo 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, ho appena ricevuto un avviso sulla boxe di un valore già inscatolato. Tuttavia, il codice per la barra è un errore di sintassi:

Errore: (7, 24) java: nessun metodo adatto trovato per valueOf (BooleanStuff.MyBoolean)
    il metodo BooleanStuff.MyBoolean.valueOf (java.lang.String) non è applicabile
      (l'argomento effettivo BooleanStuff.MyBoolean non può essere convertito in java.lang.String mediante la conversione dell'invocazione del metodo)
    il metodo java.lang.Enum.valueOf (java.lang.Class, java.lang.String) non è applicabile
      (impossibile creare un'istanza dagli argomenti perché gli elenchi di argomenti effettivi e formali differiscono per lunghezza)

Se riconduciamo l'errore di sintassi in un Stringtipo:

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

Riceviamo il nostro errore di runtime:

vero
Eccezione nel thread "main" java.lang.IllegalArgumentException: nessuna costante enum BooleanStuff.MyBoolean.false
    at java.lang.Enum.valueOf (Enum.java:236)
    su BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    su BooleanStuff.main (BooleanStuff.java:7)

Perché qualcuno dovrebbe scriverlo? Non so ... ma il suo codice funzionava e non funzionava più.


Non fraintendetemi, 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 dal codice del fornitore che assomigliava a questo:

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

che non ha mai funzionato (No, non l'ho risolto perché lo stato errato era stato risolto da qualche altra parte e risolvendo questo problema si era rotto in modi strani che non avevo davvero il tempo di eseguire il debug) . Se questo fosse un enum, quel codice avrebbe funzionato invece.

Tuttavia, le necessità della sintassi attorno all'enum (maiuscole / minuscole - scavano nella enumConstantDirectory dietro valueOf, errori di runtime che devono funzionare in quel modo per gli altri enum) e il modo in cui funzionano i metodi statici provoca una serie di cose che gli impediscono di essere un calo in sostituzione di un booleano.


1
Se uno stava progettando, da zero un nuovo linguaggio con la conoscenza di come funziona (e non funziona) Java, avere il tipo di oggetto booleano come una struttura simile a un enum ... non si adatta perfettamente a come funziona Java ora. Sono sicuro che ci sono alcuni designer linguistici che si danno dei calci per questo. Se uno potesse ricominciare da capo con Java 8 e cose come i metodi predefiniti nelle interfacce, sono sicuro che molte delle disfunzioni di Java avrebbero potuto essere fatte un po 'più pulite - allo stesso tempo apprezzo molto la capacità di accettare un codice Java 1.3 e ancora compilarlo in 1.8 - ed è qui che siamo ora.

Non sarebbe stato molto difficile aggiungere un metodo ofo frome javadoc appropriato.
assylias,

@assylias la convenzione con gran parte dell'altro codice java è valueOfe Boolean.valueOf () è lì da 1.0 . O Enums non sarebbe in grado di usare valueOf come metodo statico o Boolean avrebbe bisogno di un metodo diverso da quello che ha usato. Fare delle convenzioni o compatibilità rompe - e non avere un booleano come enum break. Da questo, la scelta è abbastanza semplice.

"Ma ... c'è un problema qui. Non puoi ignorare un metodo statico." Non stai "scavalcando" nulla: il metodo non esiste comunque in una superclasse. Il problema invece è che il metodo viene automaticamente definito per tutti gli enum e non è possibile ridefinirlo.
user102008

"Per quanto mi riguarda, ricevo solo un avvertimento sulla inscatolamento di un valore già inscatolato. Tuttavia, il codice per la barra è un errore di sintassi:" Questo è un confronto errato. In Boolean.valueOf(Boolean.valueOf("TRUE")), ci sono due diversi valueOf metodi: valueOf(String)e valueOf(boolean). L'errore di sintassi è perché hai dimenticato di implementare valueOf(boolean)in MyBoolean. Poi c'è l'autounboxing tra le due chiamate, che è codificato nella lingua per Booleanma non MyBoolean. Se hai implementato valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())funziona
user102008

2

Molto probabilmente perché il booleantipo primitivo non è un Enum, e le versioni in box dei tipi primitivi si comportano quasi in modo identico alla loro versione non in box. Per esempio

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

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

Sarebbe strano se potessi scrivere:

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

ma no:

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

1
Potresti voler mostrare anche l'istruzione if che non funzionerebbe con un enum.

@MichaelT Immagino che il compilatore sarebbe ancora in grado di decomprimerlo e fare il iflavoro proprio come fa attualmente. D'altra parte non c'è modo di ignorare il fatto che hai aggiunto funzionalità extra a quelle Booleanche booleannon hanno.
Doval,

Whoa ... non puoi scrivere istruzioni switch per i booleani in Java? Questo è pazzesco.
Thomas Eding,

Le classi di boxe agiscono solo come le primitive a causa di unboxing. Intero non ha un operatore +.
Highland Mark,

@HighlandMark È vero, ma il mio punto è che hanno sofferto molto per assicurarsi che i tipi in scatola fossero utilizzabili allo stesso modo delle loro controparti primitive. Unboxing è qualcosa che hanno dovuto implementare, non è gratuito.
Doval,

0

Oltre al valueOfproblema (che è un problema a livello di Java, potrebbe funzionare bene a livello di JVM), è perché Booleanha un costruttore pubblico. È stata una cattiva idea, attualmente deprecata, ma è quella che è qui per rimanere.


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 dall'inizio, allora avremmo potuto implementare bool come enum anziché come tipo separato".

In Swift, che avrebbe potuto esprimere "bool" come enum, esistono tre strutture denominate "Bool", "DarwinBoolean" e "ObjCBool" che implementano il protocollo "ExpressibleByBooleanLiteral". (DarwinBoolean è compatibile con un bool 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 oggetti che supportano il protocollo "ExpressibleByBooleanLiteral". Bool ha una variabile interna "_value" contenente un numero intero a un bit.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.