Recientemente me di cuenta de que hay una opción para tener métodos estáticos en las interfaces. Al igual que con los campos estáticos de interfaz, hay un comportamiento interesante: estos no se heredan.
No estoy seguro de que sea útil en las interfaces reales que se implementarán. Sin embargo, permite al programador crear interfaces que son solo sobres para cosas estáticas, como por ejemplo, las clases de utilidad.
Un ejemplo simple es solo un sobre para constantes globales. En comparación con una clase, puede notar fácilmente la repetitiva faltante, public static final
ya que se asume (lo que lo hace menos detallado).
public interface Constants {
String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
int DEFAULT_TIMEOUT_MS = 30;
}
También podría hacer algo más complejo, como este pseudoenum de claves de configuración.
public interface ConfigKeys {
static createValues(ConfigKey<?>... values) {
return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
}
static ConfigKey<T> key(Class<T> clazz) {
return new ConfigKey<>(clazz);
}
class ConfigKey<T> {
private final Class<T> type;
private ConfigKey(Class<T> type) {
this.type = type;
}
private Class<T> getType() {
return type;
}
}
}
import static ConfigKeys.*;
public interface MyAppConfigKeys {
ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
ConfigKey<String> COMPANY_NAME = key(String.class);
Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);
static values() {
return VALUES;
}
}
También podría crear alguna "clase" de utilidad de esta manera. Sin embargo, en las utilidades, a menudo es útil usar métodos auxiliares privados o protegidos, lo cual no es posible en las clases.
Considero que es una nueva característica agradable, y especialmente el hecho de que los miembros estáticos no se heredan es un concepto interesante que se introdujo solo en las interfaces.
Me pregunto si puedes considerarlo una buena práctica. Si bien el estilo de código y las mejores prácticas no son axiomáticas y hay espacio para la opinión, creo que generalmente hay razones válidas para respaldar la opinión.
Estoy más interesado en las razones (no) para usar patrones como estos dos.
Tenga en cuenta que no tengo la intención de implementar esas interfaces. Son simplemente un sobre para su contenido estático. Solo pretendo usar las constantes o métodos y posiblemente usar la importación estática.
default
métodos. Estoy hablando destatic
métodos y campos. Esos no son heredados, por lo que no rompen la herencia múltiple.Respuestas:
Poner constantes en las interfaces se llama
Constant Interface Antipattern
dado que las constantes a menudo son simplemente un detalle de implementación. Otros inconvenientes se resumen en el artículo de Wikipedia sobre la interfaz de Constant .De hecho, el documento de Java establece que los métodos estáticos pueden facilitar la organización de los métodos auxiliares, por ejemplo, al llamar a los métodos auxiliares desde los métodos predeterminados.
fuente
Como se indicó en la pregunta, la interfaz no está destinada a ser implementada (o instanciada). Entonces podríamos compararlo con una clase que tenga un constructor privado:
super()
llamar, pero la interfaz se puede "implementar"new MyInterface() {};
Esta comparación está a favor de la clase, ya que permite un mayor control. Dicho esto, la clase tampoco pretende ser un titular de material estático, es de esperar que tenga instancias. Bueno, no hay una opción perfecta.
También hay algo extraño sobre la herencia de la "interfaz estática":
Estas podrían ser razones para considerarlo un mal patrón y seguir usando clases estáticas para estos casos. Sin embargo, para alguien adicto al azúcar sintáctico, puede ser tentador incluso con las desventajas.
fuente
Como @MarkusPscheidt, esta idea es esencialmente una extensión del antipatrón Constant Interface . Este enfoque se usó inicialmente ampliamente para permitir que un conjunto único de constantes sea heredado por muchas clases dispares. La razón por la que esto terminó siendo considerado un antipatrón fue por los problemas que se encontraron al usar este enfoque en proyectos reales. No veo ninguna razón para pensar que estos problemas solo se relacionen con constantes.
Probablemente más importante, realmente no hay necesidad de hacer esto. Utilice las importaciones estáticas en su lugar y sea explícito sobre lo que está importando y desde dónde.
fuente
interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }
. Me puedo imaginar que exista algunas constantes diferentesRGB
,RGBA
,CMYK
,GREYSCALE
,INDEXED
etcYo diría que si está interesado en cómo usar la nueva función correctamente, eche un vistazo al código en el JDK. Los métodos predeterminados en las interfaces son muy utilizados y fáciles de encontrar, pero los métodos estáticos en las interfaces parecen ser muy poco comunes. Algunos ejemplos incluyen
java.time.chrono.Chronology
,java.util.Comparator
,java.util.Map
, y algunas interfaces de bastantes enjava.util.function
yjava.util.stream
.La mayoría de los ejemplos que analicé involucran usos de esas API que probablemente sean muy comunes: conversión entre diferentes tipos en la API de tiempo, por ejemplo, para comparaciones que probablemente se hagan con frecuencia entre objetos pasados a funciones u objetos contenidos en colecciones. Mi conclusión: si hay una parte de su API que necesita ser flexible, pero puede cubrir, por ejemplo, el 80% de los casos de uso con un puñado de valores predeterminados, considere usar métodos estáticos en sus interfaces. Dada la poca frecuencia con la que esta característica parece ser utilizada en el JDK, sería muy conservador al usarla en mi propio código.
Sus ejemplos no se parecen en nada a lo que veo en el JDK. Para esos casos específicos, es casi seguro que debería crear una
enum
que implemente la interfaz deseada. Una interfaz describe un conjunto de comportamientos y realmente no tiene por qué mantener una colección de sus implementaciones.fuente
¿Qué tal, um, compatibilidad con JVMs más antiguas?
No. Es un cambio retrocompatible que agrega zilch a la expresividad del lenguaje. Dos pulgares hacia abajo.
Recomiendo usar clases para contener detalles de implementación. En realidad, una interfaz solo significa "este Objeto admite operaciones XYZ" y eso no tiene nada que ver con ningún método estático que se le ocurra incluir en la declaración de la interfaz. Esta característica es como un sillón reclinable con un mini refrigerador incorporado. Claro que tiene algunos usos de nicho, pero no tiene el peso suficiente para merecer ser dominante.
ACTUALIZACIÓN: Por favor, no más votos negativos. Claramente, algunas personas piensan que los métodos estáticos en las interfaces son las rodillas de la abeja, y por la presente estoy capitulando. Prefiero eliminar esta respuesta, pero los comentarios adjuntos son una discusión interesante.
fuente