Clase booleana de Java: ¿por qué no una enumeración?

11

Me parece que la clase booleana es un candidato ideal para implementarse como una enumeración.

Mirando el código fuente, la mayoría de la clase son métodos estáticos que podrían moverse sin cambios a una enumeración, el resto se vuelve mucho más simple como una enumeración. Comparar original (comentarios y métodos estáticos eliminados):

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 versión 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";
   }
}

¿Hay alguna razón por la que Boolean no pueda convertirse en una enumeración?

Si este es el código de Sun para anular el método equals (), le falta una comprobación fundamental de comparar las referencias de los dos objetos antes de comparar sus valores. Así es como creo que debería ser el método equals ():

   public boolean equals(Object obj) {

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

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
Highland Mark
fuente
44
¿Prevé otro valor para un booleano que no sea verdadero o falso?
1
@MichaelT Una enumeración no necesita tener más de 2 valores. Sería un poco inútil en Java porque tiene una declaración especializada para el procesamiento de booleanos ( if) pero desde un punto de vista de teoría conceptual / tipo, los booleanos y las enumeraciones son instancias de tipos de suma, por lo que creo que es justo preguntar por qué no lo hicieron. No cerrar la brecha entre ellos.
Doval
1
Nota: También parece que se ha perdido la implementación de valueOf(String)(lo que entraría en conflicto con el valor de la enumeración de) y la magia detrás de la getBooleancual puede hacer que Boolean.valueOf("yes")devuelva verdadero en lugar de falso. Ambos son parte de la especificación 1.0 y necesitarían una compatibilidad con versiones anteriores adecuada.
8
@MichaelT FileNotFound, por supuesto!
Donal Fellows

Respuestas:

13

Bueno, supongo que podría comenzar argumentando que las enumeraciones de Java no se agregaron al lenguaje de programación Java hasta el JDK 1.5. y, por lo tanto, esta solución ni siquiera era una alternativa en los primeros días cuando se definió la clase booleana.

Dicho esto, Java tiene la reputación de mantener la compatibilidad con versiones anteriores entre versiones y, por lo tanto, incluso si hoy consideramos su solución como una buena alternativa, no podemos hacerlo sin romper miles de líneas de código que ya usan el viejo Boolean. clase.

edalorzo
fuente
3
Puede encontrar la especificación del lenguaje Java 1.0 para java.lang.Boolean como una ayuda. El new Boolean("True")y new Boolean("true")también puede causar algunos problemas con la implementación hipotética de enumeración.
Parece incorrecto permitir múltiples objetos (inmutables), por lo que no es una buena idea usar los Constructores proporcionados en Boolean, como dice la documentación de la API.
Highland Mark
La especificación del lenguaje no ayudaría con este tipo de preguntas, ya que no especifica las implementaciones de las clases. Esta es la mejor manera de implementar la especificación.
Highland Mark
13

Hay algunas cosas que no funcionan, y no funcionan de manera bastante sorprendente cuando se comparan con la funcionalidad anterior del booleano de Java.

Vamos a ignorar el boxeo ya que eso fue algo agregado con 1.5. Hipotéticamente, si Sun quisiera, podrían haber hecho enum Booleanque se comportara como el boxeo realizado en el class Boolean.

Sin embargo, hay otras formas sorprendentes (para el codificador) de que esto se rompería repentinamente en comparación con la funcionalidad de la clase anterior.

El problema valueOf (String)

Un ejemplo simple de esto es:

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

La ejecución de este código da:

cierto
cierto
falso
falso
Excepción en el subproceso "main" java.lang.IllegalArgumentException: Sin enumeración constante BooleanStuff.MyBoolean.FaLsE
    en java.lang.Enum.valueOf (Enum.java:236)
    en BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    en BooleanStuff.main (BooleanStuff.java:13)

El problema aquí es que no puedo pasar por todo lo que no es TRUEo FALSEque valueOf(String).

Eso está bien ... simplemente lo anularemos con nuestro propio método ...

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

Pero ... hay un problema aquí. No puede anular un método estático .

Y así, todo el código que está pasando alrededor de trueo Trueo algún otro caso se mezclan error - y de manera espectacular con una excepción de tiempo de ejecución.

Un poco más de diversión con valor

Hay otros bits que no funcionan muy bien:

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

Para foo, solo recibo una advertencia sobre el boxeo de un valor ya encuadrado. Sin embargo, el código para la barra es un error de sintaxis:

Error: (7, 24) java: no se encontró ningún método adecuado para valueOf (BooleanStuff.MyBoolean)
    El método BooleanStuff.MyBoolean.valueOf (java.lang.String) no es aplicable
      (el argumento real BooleanStuff.MyBoolean no se puede convertir a java.lang.String por conversión de invocación de método)
    El método java.lang.Enum.valueOf (java.lang.Class, java.lang.String) no es aplicable
      (no puede crear instancias de argumentos porque las listas de argumentos reales y formales difieren en longitud)

Si coercimos ese error de sintaxis nuevamente en 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);
}

Recuperamos nuestro error de tiempo de ejecución:

cierto
Excepción en el subproceso "main" java.lang.IllegalArgumentException: Sin enumeración constante BooleanStuff.MyBoolean.false
    en java.lang.Enum.valueOf (Enum.java:236)
    en BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    en BooleanStuff.main (BooleanStuff.java:7)

¿Por qué alguien escribiría eso? No sé ... pero su código solía funcionar y ya no funcionaría.


No me malinterpreten, realmente me gusta la idea de una sola copia de un objeto inmutable dado. La enumeración resuelve este problema. Personalmente, he encontrado código de proveedor que tenía errores del código de proveedor que se parecía a esto:

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

eso nunca funcionó (No, no lo arreglé porque el estado incorrecto se solucionó en otro lugar, y arreglarlo lo rompió de maneras extrañas que realmente no tuve tiempo para depurar) . Si esto fuera una enumeración, ese código habría funcionado en su lugar.

Sin embargo, las necesidades de la sintaxis alrededor de la enumeración ( distingue entre mayúsculas y minúsculas, profundice en el directorio enumConstantDirectoryvalueOf , errores de tiempo de ejecución que deben funcionar de esa manera para las otras enumeraciones) y la forma en que funcionan los métodos estáticos hace que se rompan varias cosas que impiden siendo una caída en reemplazo de un booleano.

Comunidad
fuente
1
Si uno estaba diseñando, desde cero, un nuevo lenguaje con el conocimiento de cómo funciona Java (y no lo hace), hacer que el tipo de objeto booleano sea una estructura similar a una enumeración ... simplemente no encaja con cómo funciona Java ahora. Estoy seguro de que hay algunos diseñadores de idiomas que se esfuerzan por ello. Si uno pudiera comenzar de nuevo con Java 8 y cosas como los métodos predeterminados en las interfaces, estoy seguro de que muchas de las características erróneas de Java podrían haberse hecho un poco más limpias, al mismo tiempo, realmente aprecio poder tomar un código Java 1.3 y aún lo compilamos en 1.8, y ahí es donde estamos ahora.
No habría sido muy difícil agregar un método ofo fromun javadoc apropiado.
assylias 01 de
@assylias la convención con gran parte del otro código de Java es valueOfy Boolean.valueOf () ha estado allí desde 1.0 . O Enums no podría usar valueOf como método estático, o Boolean necesitaría un método diferente al que ha estado usando. Hacer cualquiera rompe la convención o la compatibilidad, y no hacer que Boolean sea una enumeración tampoco. A partir de esto, la elección es bastante simple.
"Pero ... hay un problema aquí. No puedes anular un método estático". No está "anulando" nada; de todos modos, el método no existe en una superclase. En cambio, el problema es que el método se define automáticamente para todas las enumeraciones y no se puede redefinir.
user102008
"Para foo, acabo de recibir una advertencia sobre el boxeo de un valor ya encuadrado. Sin embargo, el código para la barra es un error de sintaxis:" Esta es una comparación incorrecta. En Boolean.valueOf(Boolean.valueOf("TRUE")), hay dos valueOf métodos diferentes : valueOf(String)y valueOf(boolean). El error de sintaxis se debe a que olvidó implementar el valueOf(boolean)in MyBoolean. Luego está el autounboxing entre las dos llamadas, que está codificado en el idioma Booleanpero no MyBoolean. Si implementó valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())trabajos
usuario102008
2

Lo más probable es que el booleantipo primitivo no sea un Enum, y las versiones en caja de los tipos primitivos se comporten de manera casi idéntica a su versión sin caja. P.ej

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

(El rendimiento puede no ser el mismo, pero ese es un tema diferente).

Sería extraño si pudieras escribir:

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

pero no:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  
Doval
fuente
1
Es posible que desee mostrar la declaración if que tampoco funcionaría con una enumeración.
@MichaelT Me imagino que el compilador aún podría desempaquetarlo y hacer el iftrabajo tal como lo hace actualmente. Por otro lado, no hay forma de ignorar el hecho de que ha agregado una funcionalidad adicional a la Booleanque booleanno tiene.
Doval
Whoa ... ¿no puedes escribir sentencias de cambio para booleanos en Java? Eso es una locura
Thomas Eding
Las clases de boxeo solo actúan como las primitivas debido al unboxing. Integer no tiene un operador +.
Highland Mark
@HighlandMark Eso es cierto, pero mi punto es que se esforzaron mucho para asegurarse de que los tipos en caja fueran utilizables de la misma manera que sus contrapartes primitivas. Unboxing es algo que tuvieron que implementar, no es gratis.
Doval
0

Además del valueOfproblema (que es un problema en el nivel Java, podría funcionar bien en el nivel JVM), es porque Booleantiene un constructor público. Esa fue una mala idea, actualmente en desuso, pero está aquí para quedarse.

Konrad Borowski
fuente
0

La razón es que "bool" era parte del lenguaje Java mucho antes que "enum". Durante muchos años, "bool" fue muy deseable, mientras que "enum" no estaba disponible. Solo que ahora puede decir "si enum hubiera estado disponible desde el principio, entonces podríamos haber implementado bool como una enumeración en lugar de un tipo separado".

En Swift, que podría haber expresado "bool" como una enumeración, hay tres estructuras llamadas "Bool", "DarwinBoolean" y "ObjCBool" que implementan el protocolo "ExpressibleByBooleanLiteral". (DarwinBoolean es compatible con un bool de C o C ++, ObjCBool ​​es compatible con un BOOL de Objective-C). "verdadero" y "falso" son valores especiales reconocidos por el compilador, y solo pueden usarse para inicializar objetos que admitan el protocolo "ExpressibleByBooleanLiteral". Bool tiene una variable interna "_value" que contiene un entero de un bit.

Entonces Bool no es parte del lenguaje Swift, sino de la biblioteca estándar. verdadero y falso son parte del lenguaje, y también lo es el protocolo ExpressibleByBooleanLiteral.

gnasher729
fuente