Java 8 métodos predeterminados como rasgos: ¿seguro?


¿Es una práctica segura usar métodos predeterminados como una versión de los rasgos de poor's man en Java 8?

Algunos afirman que puede entristecer a los pandas si los usas solo por el bien de ello, porque es genial, pero esa no es mi intención. También se recuerda a menudo que los métodos predeterminados se introdujeron para soportar la evolución de la API y la compatibilidad con versiones anteriores, lo cual es cierto, pero esto no hace que sea incorrecto o retorcido usarlos como rasgos per se.

Tengo lo siguiente caso de uso práctico en mente:

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

O tal vez, definir 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 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í. No lo soy. pedir simplemente 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 tiempo?

Author: Community, 2015-02-23

1 answers

La respuesta corta es: es seguro si los usas de forma segura:)

La respuesta sarcástica: dime qué quieres decir con rasgos, y tal vez te dé una respuesta mejor:)

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 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 comportamiento puro. Las interfaces de Java con métodos predeterminados son apátridas; ¿significa esto que no son rasgos? (Pista: esa fue una pregunta capciosa.)

De nuevo, en Scala, los rasgos se componen a través de la linealización; si la clase A extiende los rasgos X y Y, entonces el orden en el que X y Y se mezclan determina cómo se resuelven los conflictos entre X y Y. En Java, este mecanismo de linealización no está presente (fue rechazado, en parte, porque era demasiado "un-Java-como".)

La razón inmediata para agregar métodos predeterminados a las interfaces era soportar la evolución de la interfaz, pero éramos muy conscientes de que íbamos más allá de eso. Si consideras que es "interface evolution++ "o" traits traits " es una cuestión de interpretación personal. Por lo tanto, para responder a su pregunta sobre la seguridad ... siempre y cuando se adhiera a lo que el mecanismo realmente soporta, en lugar de tratar de estirarlo deseosamente a algo no es compatible, usted debe estar bien.

Un objetivo clave del diseño era que, desde la perspectiva del cliente de una interfaz, los métodos predeterminados deberían ser indistinguibles de los métodos de interfaz "regulares". El valor predeterminado de un método, por lo tanto, solo es interesante para el diseñador y el implementador de la interfaz.

Aquí hay algunos casos de uso que están bien dentro de los objetivos de diseño:

  • Evolución de la interfaz. Aquí, estamos agregar un nuevo método a una interfaz existente, que tiene una implementación por defecto sensible en términos de métodos existentes en esa interfaz. Un ejemplo sería agregar el método forEach a Collection, donde la implementación predeterminada se escribe en términos del método iterator().

  • "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 en la funcionalidad que conlleva". Para por ejemplo, a Iterator.remove se le dio un valor predeterminado que arroja UnsupportedOperationException; dado que la gran mayoría de las implementaciones de Iterator tienen este comportamiento de todos modos, el valor predeterminado hace que este método sea esencialmente opcional. (Si el comportamiento de AbstractCollection se expresara por defecto en Collection, podríamos hacer lo mismo para los métodos mutativos.)

  • Métodos de Conveniencia. Estos son métodos que son estrictamente por conveniencia, de nuevo generalmente implementados 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 instancian nuevas instancias de la interfaz basadas en la instancia actual. Por ejemplo, los métodos Predicate.and() o Comparator.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 etiqueta @implSpec 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 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 implementador predeterminado, para que el implementador pueda tomar una decisión sensata sobre si necesita reemplazarlo.

 104
Author: Brian Goetz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-25 15:37:27