variable una sirve que para modificador diferencia java

una - static final java



Interfaces con campos estáticos en Java para compartir ''constantes'' (8)

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 más sofisticados, como Scala, incluyen interfaces por rasgos que pueden (y típicamente lo hacen) incluir métodos concretos, que pueden tener una razón cero (¡constantes!). Para una exposición sobre los rasgos como unidades de comportamiento composable, ver http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf . Para una breve descripción de cómo los rasgos en Scala se comparan con las interfaces en Java, vea http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5 . En el contexto de la enseñanza del diseño OO, las reglas simplistas como afirmar que las interfaces nunca deberían incluir campos estáticos son tontas. Muchos rasgos, naturalmente, incluyen constantes y estas constantes son apropiadamente parte de la "interfaz" pública soportada por el rasgo. Al escribir código Java, no existe una manera limpia y elegante de representar rasgos, pero el uso de campos finales estáticos dentro de las interfaces a menudo es parte de una buena solución.

Estoy mirando algunos proyectos de código abierto de Java para entrar en Java y me doy cuenta de 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 tiene sentido , o una clase estática?

Me resulta extraño usar una interfaz para permitir 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 }


De acuerdo con la especificación JVM, los campos y métodos en una interfaz solo pueden tener Public, Static, Final y Abstract. Ref desde Inside Java VM

Por defecto, todos los métodos en la interfaz son abstractos, incluso difíciles de mencionar explícitamente.

Las interfaces están destinadas a dar solo especificaciones. No puede contener ninguna implementación. Entonces, para evitar implementar clases para cambiar la especificación, se hace final. Como no se puede crear una instancia de la interfaz, se convierten en estáticos para acceder al campo utilizando el nombre de la interfaz.


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:

import static com.kittens.kittenpolisher.KittenConstants.*;

Esto evita la fealdad de hacer que sus clases implementen interfaces que no tienen ninguna 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 una instancia o subclase de la clase, enviando un mensaje claro de que no contiene funcionalidades / datos no estáticos).

P.ej:

/** Set of constants needed for Kitten Polisher. */ public final class KittenConstants { private KittenConstants() {} public static final String KITTEN_SOUND = "meow"; public static final double KITTEN_CUTENESS_FACTOR = 1; }


Esto vino de un tiempo antes de que existiera Java 1.5 y nos trajo enumeraciones. Antes de eso, no había una buena manera de definir un conjunto de constantes o valores restringidos.

Esto todavía se usa, la mayoría de las veces, por compatibilidad con versiones anteriores o debido a la cantidad de refactorización necesaria para deshacerse, en una gran cantidad de proyectos.


Generalmente se considera una mala práctica. El problema es que las constantes son parte de la "interfaz" pública (a falta de una mejor palabra) de la clase implementadora. 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 en todo el código. Un ejemplo es la interfaz SwingConstants en Swing, que se implementa mediante docenas de clases que todas "reexportan" todas sus constantes (incluso las que no usan) como propias.

Pero no confíe en mi palabra, Josh Bloch también dice que es malo:

El patrón de interfaz constante es un uso deficiente de las interfaces. Que una clase use algunas constantes internamente es un detalle de implementación. La implementación de una interfaz constante hace que este detalle de implementación se filtre en la API exportada de la clase. No tiene ninguna consecuencia para los usuarios de una clase que la clase implemente una interfaz constante. De hecho, incluso puede confundirlos. Peor aún, representa un compromiso: si en una versión futura la clase se modifica para que ya no necesite usar las constantes, todavía debe implementar la interfaz para garantizar la compatibilidad binaria. Si una clase no final implementa una interfaz constante, todas sus subclases tendrán sus espacios de nombres contaminados por las constantes en la interfaz.

Una enumeración puede ser un mejor enfoque. O simplemente podría poner las constantes como campos públicos estáticos en una clase que no se puede instanciar. Esto permite que otra clase acceda a ellos sin contaminar su propia API.


Hay mucho odio por este patrón en Java. Sin embargo, una interfaz de constantes estáticas a veces tiene valor. Necesitas básicamente cumplir con las siguientes condiciones:

  1. Los conceptos son parte de la interfaz pública de varias clases.

  2. Sus valores pueden cambiar en versiones futuras.

  3. Es fundamental que todas las implementaciones utilicen los mismos valores.

Por ejemplo, supongamos que está escribiendo una extensión a un lenguaje hipotético de consulta. En esta extensión, va a expandir la sintaxis del lenguaje con algunas operaciones nuevas, que son compatibles con un índice. Por ejemplo, va a tener un R-Tree que soporte consultas geoespaciales.

Entonces, escribe una interfaz pública con la constante estática:

public interface SyntaxExtensions { // query type String NEAR_TO_QUERY = "nearTo" // params for query String POINT = "coordinate" String DISTANCE_KM = "distanceInKm" }

Ahora, más tarde, un nuevo desarrollador piensa que necesita construir un mejor índice, por lo que viene y construye una implementación R *. Al implementar esta interfaz en su nuevo árbol, garantiza que los diferentes índices tendrán una sintaxis idéntica en el lenguaje de consulta. Además, si posteriormente decidió que "nearTo" era un nombre confuso, podría cambiarlo por "withinDistanceInKm" y saber que todas las implementaciones de índice respetarían la nueva sintaxis.

PD: La inspiración para este ejemplo se extrae del código espacial de Neo4j.


No pretendo el derecho a estar en lo cierto, pero veamos este pequeño ejemplo:

public interface CarConstants { static final String ENGINE = "mechanical"; static final String WHEEL = "round"; // ... } public interface ToyotaCar extends CarConstants //, ICar, ... { void produce(); } public interface FordCar extends CarConstants //, ICar, ... { void produce(); } // and this is implementation #1 public class CamryCar implements ToyotaCar { public void produce() { System.out.println("the engine is " + ENGINE ); System.out.println("the wheel is " + WHEEL); } } // and this is implementation #2 public class MustangCar implements FordCar { public void produce() { System.out.println("the engine is " + ENGINE ); System.out.println("the wheel is " + WHEEL); } }

ToyotaCar no sabe nada sobre FordCar, y FordCar no sabe nada sobre ToyotaCar. principio CarConstants debería ser cambiado, pero ...

Las constantes no deberían cambiarse, porque la rueda es redonda y egine es mecánica, pero ... ¡En el futuro, los ingenieros de investigación de Toyota inventaron el motor electrónico y las ruedas planas! Veamos nuestra nueva interfaz

public interface InnovativeCarConstants { static final String ENGINE = "electronic"; static final String WHEEL = "flat"; // ... }

y ahora podemos cambiar nuestra abstracción:

public interface ToyotaCar extends CarConstants

a

public interface ToyotaCar extends InnovativeCarConstants

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 toquemos implementaciones

NO ES SEGURO, lo sé, pero aún quiero saber que piensas sobre esto


No tengo suficiente reputación para hacer un comentario a Pleerock, por lo tanto, tengo que crear una respuesta. Lo siento por eso, pero hizo un buen esfuerzo 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 esa implementación de autos. Son lo mismo para el cliente, solo autos. 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:

public List<Decision> compareCars(List<I_Somecar> pCars);

Una interfaz es un contrato sobre el comportamiento y muestra diferentes objetos desde una perspectiva. De la forma en que lo diseñe, cada marca de automóviles tendrá su propia línea de herencia. Aunque en realidad es bastante correcto, debido a que los automóviles pueden ser tan diferentes que puede ser como la comparación de un tipo de objeto completamente diferente, al final hay opciones entre los diferentes automóviles. 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, considera la respuesta de Zarkonnen.