¿Es una práctica segura de usar? métodos predeterminados como una versión pobre de los rasgos en Java 8?
Algunos afirman que puede entristecer a los pandas si los usas solo por el simple hecho de hacerlo, porque 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 significa que sea incorrecto o retorcido 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 se podría usar la composición (o incluso clases auxiliares) pero parece más detallada y desordenada y no permite beneficiarse del polimorfismo.
Entonces, ¿ está bien / es 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í. Tampoco estoy pidiendo meramente opiniones. 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 tiempo?
fuente
Respuestas:
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 tal como se expresan en Scala, pero Scala está lejos de ser el primer lenguaje en tener rasgos, ya sea en nombre o en efecto.
Por ejemplo, en Scala, los rasgos tienen estado (pueden tener
var
variables); en Fortress son pura conducta. Las interfaces de Java con métodos predeterminados no tienen estado; ¿Significa esto que no son rasgos? (Pista: esa fue una pregunta capciosa).De nuevo, en Scala, los rasgos se componen mediante linealización; si la clase
A
extiende rasgosX
yY
, a continuación, el orden en el queX
yY
se mezclan en determina cómo los conflictos entreX
yY
se resuelven. En Java, este mecanismo de linealización no está presente (fue rechazado, en parte, porque era demasiado "poco parecido a Java").La razón próxima para agregar métodos predeterminados a las interfaces era apoyar la evolución de la interfaz , pero sabíamos muy bien que íbamos más allá. Si lo considera "evolución de la interfaz ++" o "rasgos--" es una cuestión de interpretación personal. Entonces, para responder a su pregunta sobre seguridad ... siempre que se ciña a lo que el mecanismo realmente admite, en lugar de tratar de estirarlo 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". El carácter predeterminado de un método, por lo tanto, solo es interesante para el diseñador e implementador de la interfaz.
A continuación, se muestran 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
forEach
método aCollection
, donde la implementación predeterminada se escribe en términos deliterator()
método.Métodos "opcionales". Aquí, el diseñador de una interfaz está diciendo "Los implementadores no necesitan implementar este método si están dispuestos a vivir con las limitaciones de funcionalidad que ello implica". Por ejemplo,
Iterator.remove
se le dio un valor predeterminado que arrojaUnsupportedOperationException
; dado que 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 valores predeterminadosCollection
, podríamos hacer lo mismo con los métodos mutativos).Métodos de conveniencia. Estos son métodos que son estrictamente por conveniencia, nuevamente generalmente implementados en términos de métodos no predeterminados en la clase. El
logger()
método en su primer ejemplo es una ilustración razonable de esto.Combinadores. Estos son métodos de composición que crean instancias nuevas 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 la predeterminada (en el JDK, usamos la
@implSpec
etiqueta javadoc para esto) para ayudar a los implementadores a comprender si quieren 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 suficientes especificaciones (no solo documentación) sobre lo que promete hacer el implementador predeterminado, para que el implementador pueda tomar una decisión sensata sobre si necesita anularlo.fuente
MouseListener
? En la medida en que este estilo de API tenga sentido, encajará en el grupo de "métodos opcionales". ¡Asegúrese de documentar claramente el carácter opcional!MouseListener
. Gracias por la respuesta.