Les méthodes Java 8 par défaut interrompent-elles la compatibilité des sources?

En général, le code source de Java a été compatible avec les versions antérieures. Jusqu’à Java 8, pour autant que je sache, les classes et sources compilées étaient compatibles avec les versions ultérieures de JDK / JVM. [Mise à jour: ce n’est pas correct, voir les commentaires ci-dessous, etc.]. Cependant, avec l’ajout de méthodes par défaut dans Java 8, cela semble ne plus être le cas.

Par exemple, une bibliothèque que j’utilise a une implémentation de java.util.List qui inclut un List sort() . Cette méthode renvoie une copie du contenu de la liste sortingée. Cette bibliothèque, déployée en tant que dépendance de fichier jar, fonctionnait correctement dans un projet en cours de construction à l’aide de JDK 1.8.

Cependant, plus tard, j’ai eu l’occasion de recomstackr la bibliothèque elle-même en utilisant JDK 1.8 et j’ai constaté que la bibliothèque ne comstack plus: la classe de mise en œuvre avec sa propre méthode sort() désormais en conflit avec java.util.List.sort() méthode par défaut La méthode par défaut Java 8 sort() sortinge la liste en place (retourne un void ); La méthode sort() ma bibliothèque – puisqu’elle retourne une nouvelle liste sortingée – a une signature incompatible.

Donc, ma question de base est la suivante:

  • JDK 1.8 n’introduit-il pas une incompatibilité directe pour le code source Java en raison des méthodes par défaut?

Aussi:

  • Est-ce le premier changement incompatible de ce type?
  • Cela a-t-il été pris en compte ou discuté lorsque les méthodes par défaut ont été conçues et mises en œuvre? Est-ce documenté n’importe où?
  • Les inconvénients (certes faibles) ont-ils été réduits par rapport aux avantages?

Voici un exemple de code qui comstack et s’exécute sous 1.7 et s’exécute sous 1.8 – mais ne comstack pas sous 1.8:

 import java.util.*; public final class Sort8 { public static void main(Ssortingng[] args) { SortableList l = new SortableList(Arrays.asList(args)); System.out.println("unsorted: "+l); SortableList s = l.sort(Collections.reverseOrder()); System.out.println("sorted : "+s); } public static class SortableList extends ArrayList { public SortableList() { super(); } public SortableList(Collection col) { super(col); } public SortableList sort(Comparator cmp) { SortableList l = new SortableList(); l.addAll(this); Collections.sort(l, cmp); return l; } } } 

Ce qui suit montre ce code en cours de compilation (ou échec) et en cours d’exécution.

 > c:\tools\jdk1.7.0_10\bin\javac Sort8.java > c:\tools\jdk1.7.0_10\bin\java Sort8 this is a test unsorted: [this, is, a, test] sorted : [this, test, is, a] > c:\tools\jdk1.8.0_05\bin\java Sort8 this is a test unsorted: [this, is, a, test] sorted : [this, test, is, a] > del Sort8*.class > c:\tools\jdk1.8.0_05\bin\javac Sort8.java Sort8.java:46: error: sort(Comparator) in SortableList cannot implement sort(Comparator) in List public SortableList sort(Comparator cmp) { ^ return type SortableList is not compatible with void where V,E are type-variables: V extends Object declared in class SortableList E extends Object declared in interface List 1 error 

JDK 1.8 n’introduit-il pas une incompatibilité directe pour le code source Java en raison des méthodes par défaut?

Toute nouvelle méthode dans une super-classe ou une interface peut rompre la compatibilité. Les méthodes par défaut réduisent la probabilité qu’une modification apscope à une interface vienne briser la compatibilité. Dans le sens où les méthodes par défaut ouvrent la porte à l’ajout de méthodes aux interfaces, vous pourriez dire que les méthodes par défaut peuvent consortingbuer à une certaine rupture de compatibilité.

Est-ce le premier changement incompatible de ce type?

Presque certainement pas, puisque nous avons sous-classé les classes de la bibliothèque standard depuis Java 1.0.

Cela a-t-il été pris en compte ou discuté lorsque des méthodes par défaut ont été conçues et mises en œuvre? Est-ce documenté n’importe où?

Oui, cela a été considéré. Voir l’article de Brian Goetz daté d’août 2010 intitulé “Évolution de l’interface via les méthodes du” défenseur public “” :

  1. Compatibilité avec la source

Il est possible que ce schéma puisse introduire des incompatibilités de source dans la mesure où les interfaces de la bibliothèque sont modifiées pour insérer de nouvelles méthodes incompatibles avec les méthodes des classes existantes. (Par exemple, si une classe a une méthode xyz () à valeur flottante et implémente Collection, et que nous ajoutons une méthode xyz () à collection, la classe existante ne sera plus compilée.)

Les inconvénients (certes faibles) ont-ils été réduits par rapport aux avantages?

Auparavant, changer d’interface briserait définitivement la compatibilité. Maintenant, ça pourrait . Passer de «définitivement» à «pouvoir» peut être vu de manière positive ou négative. D’une part, il est possible d’append des méthodes aux interfaces. D’un autre côté, cela ouvre la porte à la sorte d’incompatibilité que vous avez vue, pas seulement avec les classes, mais aussi avec les interfaces.

Les avantages sont plus importants que les inconvénients, cependant, comme indiqué en haut de l’article de Goetz:

  1. Énoncé du problème

Une fois publié, il est impossible d’append des méthodes à une interface sans casser les implémentations existantes. Plus une bibliothèque a été publiée depuis longtemps, plus il est probable que cette ressortingction cause des problèmes à ses responsables.

L’ajout de fermetures au langage Java dans JDK 7 impose un stress supplémentaire aux interfaces Collection vieillissantes. L’un des avantages les plus importants des fermetures est qu’il permet de développer des bibliothèques plus puissantes. Il serait décevant d’append une fonctionnalité de langue qui permette de meilleures bibliothèques tout en n’étendant pas les bibliothèques de base pour tirer parti de cette fonctionnalité.

JDK 1.8 n’introduit-il pas une incompatibilité directe pour le code source Java en raison des méthodes par défaut?

Oui, comme vous l’avez vu vous-même.

Est-ce le premier changement incompatible de ce type?

Non, le mot-clé enum Java 5 était en train de se briser car auparavant, vous pouviez avoir des variables nommées qui ne comstackraient plus en Java 5 +.

Cela a-t-il été pris en compte ou discuté lorsque les méthodes par défaut ont été conçues et mises en œuvre? Est-ce documenté n’importe où?

Oui Orcale Java 8 description d’incompatibilité de source

Les inconvénients (certes faibles) ont-ils été réduits par rapport aux avantages?

Oui

Nous pouvons dessiner un parallèle avec la classe abstraite. Une classe abstraite est destinée à être sous-classée afin que les méthodes abstraites puissent être implémentées. La classe abstraite elle-même contient des méthodes concrètes qui invoquent les méthodes abstraites. La classe abstraite est libre d’évoluer en ajoutant des méthodes plus concrètes; et cette pratique peut casser des sous-classes.

Par conséquent, le problème exact que vous avez décrit existait même avant Java8. Le problème est beaucoup plus manifeste sur les API de collection car il existe de nombreuses sous-classes dans la nature.

Alors que la principale motivation de la méthode par défaut consistait à append des méthodes utiles aux API de collections existantes sans casser les sous-classes, elles devaient faire preuve de beaucoup de maîsortingse de soi, de peur de casser des sous-classes. Une méthode par défaut est ajoutée uniquement si cela est absolument nécessaire. La vraie question est de savoir pourquoi List.sort est considéré comme absolument nécessaire. Je pense que c’est discutable.

Indépendamment de la raison pour laquelle la méthode par défaut a été introduite à la première place, elle est désormais un excellent outil pour les concepteurs d’API, et nous devons la traiter de la même manière que les méthodes concrètes des classes abstraites. et les nouveaux doivent être introduits avec une grande prudence.

Ironiquement, les méthodes par défaut des interfaces ont été introduites pour permettre aux bibliothèques existantes utilisant ces interfaces de ne pas se casser, tout en introduisant de nouvelles fonctionnalités massives dans les interfaces. (rétrocompatibilité.)

Des conflits comme celui-ci pourraient survenir. Quelque chose à payer pour les fonctionnalités supplémentaires. Dans votre cas également, quelque chose à étudier (faut-il utiliser de nouvelles fonctionnalités à la place?).

Les ruptures de compatibilité Java à l’avance sont peu nombreuses, davantage dans son système de frappe, qui est constamment élargi. D’abord avec les types génériques et maintenant avec les types inférés des interfaces fonctionnelles. De la version à la version et du compilateur au compilateur, il y avait de légères différences.

En lisant ce numéro, je pensais à sa solution.
Les méthodes par défaut ont résolu les problèmes de compatibilité descendante mais des problèmes de compatibilité en aval existent.
Je pense qu’au lieu d’étendre les classes existantes, dans de tels cas, nous pouvons avoir nos interfaces spécifiques à l’application pour append un comportement souhaité à notre classe. Nous pouvons implémenter cette interface spécifique à l’application et l’utiliser.