Java 8 méthodes par défaut en tant que traits: sûr?

Est-ce une pratique sûre d’utiliser les méthodes par défaut en tant que version homme pauvre des traits de Java 8?

Certains prétendent que cela peut rendre les pandas sortingstes si vous les utilisez juste pour le plaisir, parce que c’est cool, mais ce n’est pas mon intention. Il est également souvent rappelé que les méthodes par défaut ont été introduites pour prendre en charge l’évolution des API et la compatibilité ascendante, ce qui est vrai, mais cela ne les rend pas fausses ou détournées pour les utiliser en tant que caractéristiques en soi.

J’ai en tête le cas d’utilisation suivant :

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

Ou peut-être définir un PeriodTrait :

 public interface PeriodeTrait { Date getStartDate(); Date getEndDate(); default isValid(Date atDate) { ... } } 

Certes, la composition pourrait être utilisée (ou même des classes d’aide) mais elle semble plus verbeuse et encombrée et ne permet pas de bénéficier du polymorphism.

Alors, est-il acceptable / sûr d’utiliser les méthodes par défaut comme traits de base ou devrais-je m’inquiéter des effets secondaires imprévus?

Plusieurs questions sur SO concernent les caractéristiques Java vs Scala; ce n’est pas le point ici. Je ne demande pas seulement des opinions non plus. Au lieu de cela, je recherche une réponse faisant autorité ou au moins un aperçu du champ: si vous avez utilisé des méthodes par défaut comme traits de caractère dans votre projet d’entreprise, s’est-il avéré être une bombe à retardement?

    La réponse courte est: c’est sûr si vous les utilisez en toute sécurité 🙂

    La réponse snarky: dites-moi ce que vous entendez par traits, et peut-être que je vous donnerai une meilleure réponse 🙂

    En toute sérieux, le terme “trait” n’est pas bien défini. De nombreux développeurs Java sont plus familiers avec les traits tels qu’ils sont exprimés dans Scala, mais Scala est loin d’être le premier langage à avoir des traits, qu’ils soient de nom ou d’effet.

    Par exemple, dans Scala, les traits sont à l’état (peuvent avoir des variables var ); dans la forteresse ils sont un comportement pur. Les interfaces Java avec les méthodes par défaut sont sans état; Est-ce que cela signifie qu’ils ne sont pas des traits? (Indice: c’était une question piège.)

    Encore une fois, à Scala, les traits sont composés par linéarisation; Si la classe A étend les traits X et Y , l’ordre dans lequel X et Y sont mélangés détermine la manière dont les conflits entre X et Y sont résolus. En Java, ce mécanisme de linéarisation n’est pas présent (il a été rejeté en partie parce qu’il était trop «non Java»).

    La raison immédiate de l’ajout de méthodes par défaut aux interfaces était de prendre en charge l’ évolution de l’interface , mais nous étions bien conscients que nous allions au-delà de cela. Que vous considériez cela comme “évolution de l’interface ++” ou “traits–” est une question d’interprétation personnelle. Donc, pour répondre à votre question sur la sécurité … tant que vous vous en tenez à ce que le mécanisme prend en charge, plutôt que d’essayer de l’étirer à quelque chose qu’il ne supporte pas, ça devrait aller.

    Un objective clé de la conception était que, du sharepoint vue du client d’une interface, les méthodes par défaut soient indiscernables des méthodes d’interface “normales”. La valeur par défaut d’une méthode n’est donc intéressante que pour le concepteur et le réalisateur de l’interface.

    Voici quelques cas d’utilisation qui sont bien dans les objectives de conception:

    • Évolution de l’interface Ici, nous ajoutons une nouvelle méthode à une interface existante, qui a une implémentation par défaut judicieuse en termes de méthodes existantes sur cette interface. Un exemple serait d’append la méthode forEach à Collection , où l’implémentation par défaut est écrite en termes de la méthode iterator() .

    • Méthodes “optionnelles”. Ici, le concepteur d’une interface dit “les implémenteurs n’ont pas besoin d’implémenter cette méthode s’ils sont prêts à vivre avec les limitations de fonctionnalités que cela implique”. Par exemple, Iterator.remove a reçu une valeur par défaut qui lance UnsupportedOperationException ; Comme la grande majorité des implémentations de Iterator ont de toute façon ce comportement, la méthode par défaut rend cette méthode essentiellement facultative. (Si le comportement de AbstractCollection était exprimé par défaut dans Collection , nous pourrions faire la même chose pour les méthodes mutatives.)

    • Méthodes de commodité. Ce sont des méthodes ssortingctement pratiques, généralement implémentées en termes de méthodes autres que celles par défaut sur la classe. La méthode logger() dans votre premier exemple en est une illustration raisonnable.

    • Combinateurs. Ce sont des méthodes de composition qui instancient de nouvelles instances de l’interface en fonction de l’instance actuelle. Par exemple, les méthodes Predicate.and() ou Comparator.thenComparing() sont des exemples de combinateurs.

    Si vous fournissez une implémentation par défaut, vous devez également fournir des spécifications pour la valeur par défaut (dans le JDK, nous utilisons la balise @implSpec pour cela) pour aider les développeurs à comprendre s’ils souhaitent ou non remplacer la méthode. Certains parameters par défaut, tels que les méthodes de commodité et les combinateurs, ne sont presque jamais remplacés. d’autres, comme les méthodes optionnelles, sont souvent remplacées. Vous devez fournir suffisamment de spécifications (pas seulement de la documentation) sur ce que la valeur par défaut promet de faire, afin que l’implémenteur puisse prendre une décision éclairée quant à la nécessité de le remplacer.