poner - Métodos predeterminados de Java 8 como rasgos: ¿seguro?
setbounds java (1)
¿Es una práctica segura usar los métodos predeterminados como una versión para hombres pobres de los rasgos en Java 8?
Algunos afirman que puede hacer que los pandas se pongan tristes si los usas solo por el hecho de que es genial, pero esa no es mi intención. También se recuerda a menudo que se introdujeron métodos predeterminados para admitir la evolución de la API y la compatibilidad con versiones anteriores, lo cual es cierto, pero esto no hace que sea incorrecto o torcido usarlos como rasgos per se.
Tengo en mente el siguiente caso de uso práctico :
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
O quizás, defina un
PeriodTrait
:
public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}
Es cierto que la composición podría usarse (o incluso clases auxiliares), pero parece más prolijo y desordenado, y no permite beneficiarse del polimorfismo.
Entonces, ¿ está bien / seguro usar métodos predeterminados como rasgos básicos , o debería preocuparme por los efectos secundarios imprevistos?
Varias preguntas sobre SO están relacionadas con los rasgos de Java vs Scala; ese no es el punto aquí. No estoy pidiendo meramente opiniones tampoco. En cambio, estoy buscando una respuesta autorizada o al menos una visión de campo: si ha utilizado métodos predeterminados como rasgos en su proyecto corporativo, ¿resultó ser una bomba de relojería?
La respuesta corta es: es seguro si los usa de manera segura :)
La respuesta sarcástica: dime qué quieres decir con rasgos, y tal vez te dé una mejor respuesta :)
Con toda seriedad, el término "rasgo" no está bien definido. Muchos desarrolladores de Java están más familiarizados con los rasgos, ya que se expresan en Scala, pero Scala está lejos de ser el primer idioma en tener rasgos, ya sea en nombre o en efecto.
Por ejemplo, en Scala, los rasgos tienen estado (pueden tener variables
var
);
en Fortress son puro comportamiento.
Las interfaces de Java con métodos predeterminados no tienen estado;
¿Esto significa que no son rasgos?
(Sugerencia: esa fue una pregunta capciosa).
Nuevamente, en Scala, los rasgos se componen a través de la linealización;
si la clase
A
extiende los rasgos
X
e
Y
, entonces el orden en el que se mezclan
X
e
Y
determina cómo se resuelven los conflictos entre
X
e
Y
En Java, este mecanismo de linealización no está presente (fue rechazado, en parte, porque era demasiado "similar a Java").
La razón inmediata para agregar métodos predeterminados a las interfaces era apoyar la evolución de la interfaz , pero sabíamos que íbamos más allá de eso. Si considera que se trata de "evolución de la interfaz ++" o "rasgos", es una cuestión de interpretación personal. Entonces, para responder a su pregunta sobre seguridad ... siempre y cuando se apegue a lo que el mecanismo realmente admite, en lugar de tratar de extenderlo a algo que no admite, debería estar bien.
Un objetivo de diseño clave era que, desde la perspectiva del cliente de una interfaz, los métodos predeterminados no deberían distinguirse de los métodos de interfaz "normales". La predeterminación de un método, por lo tanto, solo es interesante para el diseñador e implementador de la interfaz.
Aquí hay algunos casos de uso que están dentro de los objetivos de diseño:
-
Evolución de la interfaz. Aquí, estamos agregando un nuevo método a una interfaz existente, que tiene una implementación predeterminada sensible en términos de métodos existentes en esa interfaz. Un ejemplo sería agregar el método
forEach
aCollection
, donde la implementación predeterminada se escribe en términos del métodoforEach
iterator()
. -
Métodos "opcionales". Aquí, el diseñador de una interfaz dice "Los implementadores no necesitan implementar este método si están dispuestos a vivir con las limitaciones de funcionalidad que conlleva". Por ejemplo,
Iterator.remove
recibió un valor predeterminado que arrojaUnsupportedOperationException
; Como la gran mayoría de las implementaciones deIterator
tienen este comportamiento de todos modos, el valor predeterminado hace que este método sea esencialmente opcional. (Si el comportamiento deAbstractCollection
se expresara como predeterminado enCollection
, podríamos hacer lo mismo con los métodos mutativos). -
Métodos de conveniencia. Estos son métodos estrictamente para conveniencia, nuevamente implementados generalmente en términos de métodos no predeterminados en la clase. El método
logger()
en su primer ejemplo es una ilustración razonable de esto. -
Combinadores Estos son métodos de composición que crean instancias de nuevas instancias de la interfaz en función de la instancia actual. Por ejemplo, los métodos
Predicate.and()
oComparator.thenComparing()
son ejemplos de combinadores.
Si proporciona una implementación predeterminada, también debe proporcionar alguna especificación para el valor predeterminado (en el JDK, usamos la etiqueta javadoc
@implSpec
para esto) para ayudar a los implementadores a comprender si desean anular el método o no.
Algunos valores predeterminados, como los métodos de conveniencia y los combinadores, casi nunca se anulan;
otros, como los métodos opcionales, a menudo se anulan.
Debe proporcionar suficiente especificación (no solo documentación) sobre lo que promete hacer el predeterminado, para que el implementador pueda tomar una decisión sensata sobre si necesita anularlo.