Estoy mirando algunos proyectos Java de código abierto para ingresar a Java y noto que muchos de ellos tienen algún tipo de interfaz de 'constantes'.
Por ejemplo, processing.org tiene una interfaz llamada PConstants.java , y la mayoría de las otras clases principales implementan esta interfaz. La interfaz está plagada de miembros estáticos. ¿Hay alguna razón para este enfoque o se considera una mala práctica? ¿Por qué no usar enumeraciones donde tenga sentido , o una clase estática?
Me parece extraño usar una interfaz que permita algún tipo de pseudo 'variables globales'.
public interface PConstants {
// LOTS OF static fields...
static public final int SHINE = 31;
// emissive (by default kept black)
static public final int ER = 32;
static public final int EG = 33;
static public final int EB = 34;
// has this vertex been lit yet
static public final int BEEN_LIT = 35;
static public final int VERTEX_FIELD_COUNT = 36;
// renderers known to processing.core
static final String P2D = "processing.core.PGraphics2D";
static final String P3D = "processing.core.PGraphics3D";
static final String JAVA2D = "processing.core.PGraphicsJava2D";
static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
static final String PDF = "processing.pdf.PGraphicsPDF";
static final String DXF = "processing.dxf.RawDXF";
// platform IDs for PApplet.platform
static final int OTHER = 0;
static final int WINDOWS = 1;
static final int MACOSX = 2;
static final int LINUX = 3;
static final String[] platformNames = {
"other", "windows", "macosx", "linux"
};
// and on and on
}
static final
no es necesario, es redundante para una interfaz.platformNames
puede haberpublic
,static
yfinal
, pero definitivamente no es una constante. La única matriz constante es una con longitud cero.static final
no es necesariamente redundante. Un campo de clase o interfaz con solo lafinal
palabra clave crearía instancias separadas de ese campo a medida que crea objetos de la clase o interfaz. El usostatic final
haría que cada objeto compartiera una ubicación de memoria para ese campo. En otras palabras, si una clase MyClass tuviera un campofinal String str = "Hello";
, para N instancias de MyClass, habría N instancias del campo str en la memoria. Agregar lastatic
palabra clave daría como resultado una sola instancia.Respuestas:
Generalmente se considera una mala práctica. El problema es que las constantes son parte de la "interfaz" pública (a falta de una palabra mejor) de la clase de implementación. Esto significa que la clase de implementación está publicando todos estos valores en clases externas incluso cuando solo se requieren internamente. Las constantes proliferan a lo largo del código. Un ejemplo es la interfaz SwingConstants en Swing, que es implementada por docenas de clases que todas "reexportan" todas sus constantes (incluso las que no usan) como propias.
Pero no solo confíe en mi palabra, Josh Bloch también dice que es malo:
Una enumeración puede ser un mejor enfoque. O simplemente podría poner las constantes como campos estáticos públicos en una clase de la que no se puede crear una instancia. Esto permite que otra clase acceda a ellos sin contaminar su propia API.
fuente
En lugar de implementar una "interfaz de constantes", en Java 1.5+, puede usar importaciones estáticas para importar las constantes / métodos estáticos de otra clase / interfaz:
Esto evita la fealdad de hacer que sus clases implementen interfaces que no tienen funcionalidad.
En cuanto a la práctica de tener una clase solo para almacenar constantes, creo que a veces es necesario. Hay ciertas constantes que simplemente no tienen un lugar natural en una clase, por lo que es mejor tenerlas en un lugar "neutral".
Pero en lugar de usar una interfaz, use una clase final con un constructor privado. (Haciendo imposible crear instancias o subclases de la clase, enviando un fuerte mensaje de que no contiene funcionalidad / datos no estáticos).
P.ej:
fuente
No pretendo tener razón, pero veamos este pequeño ejemplo:
ToyotaCar no sabe nada sobre FordCar y FordCar no sabe nada sobre ToyotaCar. El principio CarConstants debería cambiarse, pero ...
Las constantes no deben cambiarse, porque la rueda es redonda y el motor es mecánico, pero ... ¡En el futuro, los ingenieros de investigación de Toyota inventaron el motor electrónico y las ruedas planas! Veamos nuestra nueva interfaz
y ahora podemos cambiar nuestra abstracción:
a
Y ahora, si alguna vez necesitamos cambiar el valor central, si el MOTOR o la RUEDA, podemos cambiar la interfaz ToyotaCar en el nivel de abstracción, no toque las implementaciones.
NO ES SEGURO, lo sé, pero aún quiero saber si piensas en esto
fuente
Hay mucho odio por este patrón en Java. Sin embargo, una interfaz de constantes estáticas a veces tiene valor. Básicamente, debes cumplir las siguientes condiciones:
Los conceptos forman parte de la interfaz pública de varias clases.
Sus valores pueden cambiar en versiones futuras.
Por ejemplo, suponga que está escribiendo una extensión para un lenguaje de consulta hipotético. En esta extensión vas a expandir la sintaxis del lenguaje con algunas operaciones nuevas, que son soportadas por un índice. Por ejemplo, tendrá un R-Tree que soporte consultas geoespaciales.
Entonces escribe una interfaz pública con la constante estática:
Ahora, más tarde, un nuevo desarrollador piensa que necesita crear un índice mejor, por lo que viene y crea una implementación R *. Al implementar esta interfaz en su nuevo árbol, garantiza que los diferentes índices tendrán la misma sintaxis en el lenguaje de consulta. Además, si más tarde decidió que "nearTo" era un nombre confuso, podría cambiarlo a "withinDistanceInKm" y saber que la nueva sintaxis sería respetada por todas sus implementaciones de índices.
PD: La inspiración para este ejemplo se extrae del código espacial de Neo4j.
fuente
Dada la ventaja de la retrospectiva, podemos ver que Java está roto de muchas maneras. Una falla importante de Java es la restricción de interfaces a métodos abstractos y campos finales estáticos. Los lenguajes OO más nuevos y sofisticados, como Scala, subsumen interfaces por rasgos que pueden (y normalmente lo hacen) incluir métodos concretos, que pueden tener arity cero (¡constantes!). Para obtener una exposición sobre los rasgos como unidades de comportamiento componible, consulte http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf . Para obtener una breve descripción de cómo se comparan los rasgos en Scala con las interfaces en Java, consulte http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5. En el contexto de la enseñanza del diseño de OO, las reglas simplistas como afirmar que las interfaces nunca deben incluir campos estáticos son tontas. Muchos rasgos incluyen naturalmente constantes y estas constantes son apropiadamente parte de la "interfaz" pública soportada por el rasgo. Al escribir código Java, no existe una forma limpia y elegante de representar rasgos, pero el uso de campos finales estáticos dentro de las interfaces suele ser parte de una buena solución.
fuente
De acuerdo con la especificación de JVM, los campos y métodos en una interfaz solo pueden tener Público, Estático, Final y Resumen. Referencia desde dentro de Java VM
De forma predeterminada, todos los métodos en la interfaz son abstractos, incluso aunque no lo hayas mencionado explícitamente.
Las interfaces están destinadas a proporcionar solo especificaciones. No puede contener implementaciones. Entonces, para evitar implementar clases para cambiar la especificación, se hace final. Dado que no se puede crear una instancia de Interface, se vuelven estáticos para acceder al campo usando el nombre de la interfaz.
fuente
No tengo la reputación suficiente para dar un comentario a Pleerock, por lo que tengo que crear una respuesta. Lo siento, pero se esforzó mucho y me gustaría responderle.
Pleerock, creó el ejemplo perfecto para mostrar por qué esas constantes deberían ser independientes de las interfaces e independientes de la herencia. Para el cliente de la aplicación no es importante que exista una diferencia técnica entre la implementación de los automóviles. Son lo mismo para el cliente, solo coches. Entonces, el cliente quiere verlos desde esa perspectiva, que es una interfaz como I_Somecar. A lo largo de la aplicación, el cliente utilizará solo una perspectiva y no diferentes para cada marca de automóvil diferente.
Si un cliente quiere comparar autos antes de comprar, puede tener un método como este:
Una interfaz es un contrato sobre el comportamiento y muestra diferentes objetos desde una perspectiva. La forma en que lo diseñe, ¿tendrá cada marca de automóvil su propia línea de herencia? Aunque en realidad es bastante correcto, porque los autos pueden ser tan diferentes que puede ser como comparar tipos de objetos completamente diferentes, al final hay que elegir entre diferentes autos. Y esa es la perspectiva de la interfaz que todas las marcas deben compartir. La elección de constantes no debería hacer esto imposible. Por favor, considere la respuesta de Zarkonnen.
fuente
Esto vino de una época anterior a la existencia de Java 1.5 y nos trajo enumeraciones. Antes de eso, no había una buena forma de definir un conjunto de constantes o valores restringidos.
Esto todavía se usa, la mayoría de las veces, ya sea por compatibilidad con versiones anteriores o debido a la cantidad de refactorización necesaria para deshacerse, en muchos proyectos.
fuente