La réponse courte est: c'est sûr si vous les utilisez en toute sécurité :)
La réponse sournoise: dites-moi ce que vous entendez par traits, et peut-être que je vais vous donner une meilleure réponse :)
Sérieusement, 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 en Scala, mais Scala est loin d'être le premier langage à avoir des traits, que ce soit en nom ou en effet.
Par exemple, dans Scala, les traits sont avec état (peuvent avoir des var
variables); dans Fortress, ce sont des comportements purs. Les interfaces de Java avec les méthodes par défaut sont sans état; cela veut-il dire que ce ne sont pas des traits? (Indice: c'était une question piège.)
Encore une fois, dans Scala, les traits sont composés par linéarisation; si la classe A
étend les traits X
et Y
, alors l'ordre dans lequel X
et Y
sont mélangés détermine comment 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, car il était trop "différent de Java".)
La raison immédiate de l'ajout de méthodes par défaut aux interfaces était de prendre en charge l' évolution des interfaces , mais nous étions bien conscients que nous allions plus loin. Que vous considériez cela comme une "évolution d'interface ++" ou des "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 soutient réellement, plutôt que d'essayer de l'étirer à souhait à quelque chose qu'il ne supporte pas, tout devrait aller bien.
Un objectif clé de la conception était que, du point de vue du client d'une interface, les méthodes par défaut ne devraient pas être distinguées des méthodes d'interface "régulières". La valeur par défaut d'une méthode n'est donc intéressante que pour le concepteur et l' implémenteur de l'interface.
Voici quelques cas d'utilisation qui correspondent bien aux objectifs de conception:
Évolution de l'interface. Ici, nous ajoutons une nouvelle méthode à une interface existante, qui a une implémentation par défaut sensible en termes de méthodes existantes sur cette interface. Un exemple serait l'ajout de la forEach
méthode à Collection
, où l'implémentation par défaut est écrite en termes de iterator()
méthode.
Méthodes "facultatives". Ici, le concepteur d'une interface dit: "Les réalisateurs n'ont pas besoin d'implémenter cette méthode s'ils veulent vivre avec les limitations de fonctionnalités que cela implique". Par exemple, Iterator.remove
a reçu une valeur par défaut qui jette UnsupportedOperationException
; puisque la grande majorité des implémentations Iterator
ont de toute façon ce comportement, la valeur par défaut rend cette méthode essentiellement facultative. (Si le comportement de AbstractCollection
était exprimé par défaut sur Collection
, nous pourrions faire de même pour les méthodes mutatives.)
Méthodes de commodité. Ce sont des méthodes strictement pratiques, à nouveau généralement implémentées en termes de méthodes non par défaut sur la classe. La logger()
méthode de 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 une spécification pour @implSpec
la méthode par défaut (dans le JDK, nous utilisons la balise javadoc pour cela) pour aider les implémenteurs à comprendre s'ils veulent remplacer la méthode ou non. Certaines valeurs par défaut, comme les méthodes pratiques et les combinateurs, ne sont presque jamais remplacées; d'autres, comme les méthodes facultatives, sont souvent remplacées. Vous devez fournir suffisamment de spécification (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 judicieuse pour savoir s'il doit le remplacer.