¿Cómo se define una clase de constantes en Java?

89

Suponga que necesita definir una clase que todo lo que hace es mantener constantes.

public static final String SOME_CONST = "SOME_VALUE";

¿Cuál es la forma preferida de hacer esto?

  1. Interfaz
  2. Clase abstracta
  3. Clase final

¿Cuál debo usar y por qué?


Aclaraciones a algunas respuestas:

Enums : no voy a usar enumeraciones, no voy a enumerar nada, solo recopilar algunas constantes que no están relacionadas entre sí de ninguna manera.

Interfaz : no voy a establecer ninguna clase como una que implemente la interfaz. Sólo quieren utilizar la interfaz para llamar constantes de este modo: ISomeInterface.SOME_CONST.

Yuval Adam
fuente
Hay una discusión similar aquí: stackoverflow.com/questions/320588/… . Usaría una clase final con un constructor privado para que no se pueda crear una instancia.
Dan Dyer
2
Lo siento, pero "No voy a usar enumeraciones" convierte esta pregunta en "¿Cuál es la mejor manera de hacer algo estúpido?"
cletus
No digo que vaya a implementar la interfaz. Pero no tiene sentido usar una interfaz para hacer eso. Entonces, vaya con la clase final
:)
¿Cuál es el problema con Enum? Siempre puede usarlo para recopilar "algunas constantes que no están relacionadas entre sí de ninguna manera". ¿Hmm?
gedevan
5
Conceptualmente, una enumeración es una mala elección si las constantes no están relacionadas. Una enumeración representa valores alternativos del mismo tipo. Estas constantes no son alternativas y es posible que ni siquiera sean del mismo tipo (algunas pueden ser cadenas, algunos enteros, etc.)
Dan Dyer

Respuestas:

92

Utilice una clase final. para simplificar, puede usar una importación estática para reutilizar sus valores en otra clase

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

en otra clase:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
user54579
fuente
4
¿Dónde está el beneficio de crear una clase separada aquí? Aunque normalmente no considero la API de calendario como un buen ejemplo de diseño, está bien en términos de "las constantes relacionadas con el calendario están en la clase Calendario".
Jon Skeet
7
el beneficio es no duplicar el código, en caso de que necesite reutilizar constantes en más de una clase. Supongo que puedes ver fácilmente la ventaja de esto.
user54579
2
¿Por qué duplicarías el código? Consulte la otra clase. Me parece relativamente raro que una constante realmente esté sola.
Jon Skeet
14
Para una mejor legibilidad, también tendría un constructor predeterminado privado sin cuerpo (y un comentario coincidente).
Ran Biron
5
Respuesta bastante antigua, y este comentario probablemente esté fuera de lugar, pero lo usaría VALUE1.equals(variable)ya que evita NPE.
Aakash
38

Su aclaración dice: "No voy a usar enumeraciones, no voy a enumerar nada, solo recopilar algunas constantes que no están relacionadas entre sí de ninguna manera".

Si las constantes no están relacionadas entre sí en absoluto, ¿por qué quiere recopilarlas? Coloque cada constante en la clase con la que esté más estrechamente relacionada.

Jon Skeet
fuente
2
Jon: están relacionados en el sentido de que todos pertenecen a la misma funcionalidad. Sin embargo, no son una enumeración de nada ... No tienen la propiedad de que un objeto sea "una de esas cosas".
Yuval Adam
13
Bueno. En ese caso, colóquelos en la clase más cercana a la funcionalidad con la que están relacionados.
Jon Skeet
26

Mis sugerencias (en orden decreciente de preferencia):

1) No lo hagas . Cree las constantes en la clase real donde sean más relevantes. Tener una clase / interfaz 'bolsa de constantes' no está realmente siguiendo las mejores prácticas de OO.

Yo, y todos los demás, ignoramos el número 1 de vez en cuando. Si vas a hacer eso, entonces:

2) clase final con constructor privado Esto al menos evitará que alguien abuse de su 'bolsa de constantes' extendiéndola / implementándola para tener fácil acceso a las constantes. (Sé que dijiste que no harías esto, pero eso no significa que alguien venga después de ti no lo hará)

3) interfaz Esto funcionará, pero no es mi preferencia, dando la posible mención de abuso en el n. ° 2.

En general, el hecho de que sean constantes no significa que no deba aplicarles los principios normales de oo. Si a nadie más que a una clase le importa una constante, debería ser privada y en esa clase. Si solo las pruebas se preocupan por una constante, debería ser una clase de prueba, no un código de producción. Si una constante se define en varios lugares (no solo accidentalmente el mismo), refactorice para eliminar la duplicación. Y así sucesivamente, trátelos como si fuera un método.

kenj0418
fuente
2
El problema del 1 es que a veces inyectarás en tu código algunas dependencias a otra clase de nivel solo para recuperar la constante.
amdev
Todos los campos en una interfaz son implícitamente públicos, estáticos y finales
Kodebetter
@Kodebetter Pero las interfaces se pueden extender / implementar para agregar más campos.
Wit
12

Como señala Joshua Bloch en Effective Java:

  • Las interfaces solo deben usarse para definir tipos,
  • las clases abstractas no evitan la instanciabilidad (se pueden subclasificar e incluso sugerir que están diseñadas para ser subclasificadas).

Puede usar un Enum si todas sus constantes están relacionadas (como los nombres de los planetas), poner los valores constantes en las clases con las que están relacionados (si tiene acceso a ellos), o usar una clase de utilidad no instanciable (defina un constructor predeterminado privado) .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

Luego, como ya se indicó, puede usar importaciones estáticas para usar sus constantes.

Sébastien RoccaSerra
fuente
7

Mi método preferido es no hacer eso en absoluto. La era de las constantes prácticamente murió cuando Java 5 introdujo las enumeraciones con seguridad de tipos. E incluso antes de eso, Josh Bloch publicó una versión (un poco más prolija) de eso, que funcionaba en Java 1.4 (y versiones anteriores).

A menos que necesite interoperabilidad con algún código heredado, realmente ya no hay razón para usar constantes String / integer con nombre.

cletus
fuente
2
Buena respuesta, un ejemplo sería mejor.
amdev
3

Solo usa la clase final.

Si desea poder agregar otros valores, use una clase abstracta.

No tiene mucho sentido usar una interfaz, se supone que una interfaz especifica un contrato. Solo desea declarar algunos valores constantes.

Megacan
fuente
3

¿No son las enumeraciones la mejor opción para este tipo de cosas?

Boris Pavlović
fuente
2
99% del tiempo. Pero no siempre.
L. Holanda
3

enums están bien. IIRC, un elemento en Java efectivo (2.a edición) tieneenum constantes que enumeran opciones estándar que implementan una [palabra clave de Java] interfacepara cualquier valor.

Mi preferencia es usar una [palabra clave de Java] interfacesobre una final classpara constantes. Implícitamente obtienes el public static final. Algunas personas argumentarán que uninterface permite que los malos programadores lo implementen, pero los malos programadores van a escribir código que apesta sin importar lo que hagas.

¿Cuál luce mejor?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

O:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Tom Hawtin - tackline
fuente
pero los malos programadores van a escribir código que apesta sin importar lo que hagas. Eso es tan cierto. Si puede tomar medidas para mitigar la mala programación o eliminarla por completo, entonces tiene un poco de responsabilidad para hacerlo; Sea lo más explícito posible. Un mal programador no puede extender una clase final.
liltitus27
1
@ liltitus27 Hasta cierto punto estoy de acuerdo. Pero, ¿realmente quieres un código desagradable solo para evitar que los programadores pobres escriban código incorrecto? El código que producen seguirá siendo inútil, pero ahora su código es menos legible.
Tom Hawtin - tackline el
1

O 4. Colóquelos en la clase que contiene la lógica que usa las constantes más

... lo siento, no pude resistir ;-)

Simon Groenewolt
fuente
3
No es buena idea. Esto crea dependencias entre clases que no deberían tener ninguna.
Yuval Adam
Por qué no? Yo diría que las clases que usan las mismas constantes tienen una dependencia de todos modos ... Y de alguna manera debe haber una clase que sea más dependiente de estas constantes.
Simon Groenewolt
Si solo hubiera una clase usando las constantes, esto podría tener sentido.
Tom Hawtin - tackline el
-1
  1. Una de las desventajas del constructor privado es que el método existente nunca podría probarse.

  2. Enum por el concepto de naturaleza bueno para aplicar en un tipo de dominio específico, aplicarlo a constantes descentralizadas no parece lo suficientemente bueno

El concepto de Enum es "Las enumeraciones son conjuntos de elementos estrechamente relacionados".

  1. Extender / implementar una interfaz constante es una mala práctica, es difícil pensar en el requisito de extender una constante inmutable en lugar de referirse a ella directamente.

  2. Si aplica una herramienta de calidad como SonarSource, hay reglas que obligan al desarrollador a eliminar la interfaz constante, esto es algo incómodo ya que muchos proyectos disfrutan de la interfaz constante y rara vez se ven "extender" las cosas que suceden en interfaces constantes

Guizhou Feng
fuente
1
"El constructor privado es la existencia de un método que nunca podría probarse": no es cierto. Si usted (o su jefe) está realmente paranoico acerca de que los informes de cobertura de código sean del 100%, aún puede probar esto utilizando la reflexión y asegurándose de que el constructor arroje una excepción. Pero, en mi humilde opinión, esto es solo una formalidad y realmente no necesita ser probado. Si usted (o su equipo) solo se preocupa realmente por lo que es realmente importante, la cobertura de línea al 100% no es necesaria.
L. Holanda