Pourquoi ils intègrent l'API de flux au framework de collecte en java 8


Lors de l'apprentissage des modèles de conception, j'ai entendu dire que la délégation est meilleure que l'héritage dans la plupart des cas.

Je me demande donc pourquoi l'équipe java8 a pris la décision d'intégrer l'API Stream dans le framework de collections existant au lieu d'utiliser la délégation (construire un flux basé sur la collection donnée)?

En particulier, ils doivent introduire le nouveau concept d'implémentation de la méthode par défaut d'Interface qui, à son tour, estompe la sémantique des Interfaces par rapport aux interfaces. Les classes abstraites?

Author: GhostCat, 2017-07-15

5 answers

La délégation vaut mieux que l'héritage

Je pense que vous avez écrit du mal dans votre question. En fait, la forme correcte est Composition au cours de l'Héritage.

En Effet, Collection#stream & Collection#spliterator est conçu pour l'application de Méthode de Fabrique modèle. ce qui signifie que les sous-classes peuvent le posséder Stream/Spliteartor instance pour activer features et promouvoir l'objectif performance en java.

SI il n'y a pas de telles méthodes d'usine dans Collection, vous devez revenir au code de procédure et vérifier le type réel pour créer des Stream appropriés lors de l'exécution.

Vous ne voyez que les méthodes par défaut déclarées sur Collection, avez-vous vu les méthodes de remplacement sur les sous-classes, par exemple:

Les Collections de#nCopies utilise CopiesList comme ci-dessous pour créer un Stream<E> par IntStream pour promouvoir la performance.

public Stream<E> stream() {
   return IntStream.range(0, n).mapToObj(i -> element);
}

ArrayList#spliterator utilise ArrayListSpliterator comme ci-dessous pour créer un fail-fast spliterator:

public Spliterator<E> spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}
 4
Author: holi-java, 2017-07-15 15:06:50

Tout d'abord, la prise en charge des méthodes default et static dans les interfaces n'a pas été ajoutée uniquement pour prendre en charge la méthode stream() dans l'interface Collection.

C'est un désir naturel de fournir des méthodes d'utilité et des valeurs par défaut utiles lors de la définition de l'interface, conduisant au modèle d'avoir deux classes différentes pour les héberger, l'interface et une classe d'utilité associée. Dans le cas de valeurs par défaut utiles, les implémenteurs devaient toujours écrire une méthode de délégation pour les utiliser. Bien sûr, ce serait même s'aggraver avec des interfaces fonctionnelles limitées à une seule méthode. Voir aussi cette réponse.

Donc, lorsque vous considérez la fonctionnalité de langue des méthodes par défaut comme déjà existante, le choix de l'utiliser dans l'API de collection pour plus de commodité n'est pas si grand. C'est toujours une délégation, juste avec un point d'entrée pratique. Ce n'est pas différent de, par exemple String.format(…) déléguer à l'installation java.util.Formatter ou String.replaceAll déléguer à java.util.regex.

Mais l'API Stream ne peut pas s'exécuter sur son propre sans aucun support actif du côté de l'API de collection. Le minimum requis est un Iterator du Collection. Le Iterator a été remplacé par Spliterator pour l'API Stream, mais si un Collection fournit un Iterator ou un Spliterator n'est pas un changement de conception fondamental. C'est quelque chose qui relève de la responsabilité de la collection. Il existe une méthode default créant un Spliterator à partir d'un Iterator pour permettre à chaque collection existante de fonctionner avec le nouveau framework prêt à l'emploi, mais chaque implémentation Collection a la possibilité de remplacer cette méthode, fournissant une implémentation Spliterator mieux adaptée et potentiellement plus efficace. La plupart des implémentations de collection standard de JRE et de nos jours beaucoup de collections tierces utilisent cette opportunité.

Être overridable est une propriété des méthodes default que vous ne pouvez pas émuler avec des méthodes static dans une autre classe ou package. De sorte qu'il fonctionne dans l'autre sens. Par exemple, vous pouvez toujours invoquer les anciennes méthodes Collections.sort, mais elles délégueront à la nouveau List.sort default méthode, qui peut être remplacée par une implémentation plus efficace.

Comme vous normalement ne pas traiter avec un Iterator manuellement lors de l'utilisation de for(Variable v: collection) …, vous ne travaillez pas avec un Spliterator manuellement, lors de l'utilisation de collection.stream(). … .

 3
Author: Holger, 2017-07-17 08:36:06

Fournir un framework destiné à être utilisé par des millions d'utilisateurs consiste toujours à équilibrer la facilité d'utilisation et à suivre des directives strictes .

Et le tout premier point à comprendre: favoriser la composition sur l'héritageest une bonne pratique. Mais cela ne signifie pas que toujours préfère composition.

L'architecture new streams est conçue comme un nouveau bloc de construction core des API Java. Dans ce sens: c'est un choix naturel de permettre de transformer des collections en flux via une fonction membre.

Au-delà de cela, il semble que les personnes derrière le langage java favorisent les interfaces fluent. En ce sens, ils préfèrent probablement

list.stream(). stream operations 

Sur

Streams.stream(list). stream operations

Et bien sûr: quand vous pensez à la seule suggestion que j'ai formulée ici, commentalternativement implémenter une conversion de list en stream-cela ne fonctionnerait que pour les collections connues classe. Donc un autre concept serait nécessaire, comme

YourSpecialStreamCreator.(yourSpecialCollection).stream()

Alors que le fait que la méthode interface basée sur stream () vous permet simplement de @Override l'implémentation par défaut lorsque votre implémentation de collection spéciale a besoin de le faire. Mais vous pouvez toujours utiliser toutes les autres choses autour des flux dans cette interface!

Au-delà de cela: l'idée de méthodes par défaut dans les interfaces aide en fait à de nombreux problèmes. Oui, ils étaient nécessaires pour ajouter ces nouvelles fonctionnalités à l'interface - mais diable: ajouter de nouvelles méthodes aux interfaces est quelque chose que la plupart des gens aimeraient faire à un moment donné. Donc, avoir unclair , défini de manière soutenue dans le langage de base est extrêmement utile.

Mes deux cents personnels ici: ajout de V2, V3,... interfaces comme:

interface MyFunctionality { ...

interface MyFunctionalityV2 extends MyFunctionality { 
  void someNewThing();

Est juste laid et douloureux.

 2
Author: GhostCat, 2017-07-17 07:45:10

Pourquoi ils intègrent l'API stream au framework de collecte en java 8

Parce qu'avant Java 8, les fonctionnalités des collections Java avaient un retard important sur les langages concurrents tels que C#, Scala, Ruby, etc... cela fournit hors de la boîte et la plupart du temps de manière concise un riche ensemble de méthodes fonctionnelles pour les collections, mais aussi le traitement de pipeline pour les collections.

Je me demande donc pourquoi l'équipe java8 a pris la décision d'intégrer Stream API dans l'existant Cadre de collections au lieu d'utiliser délégation (construire un flux basé sur la collection donnée)?

Cela rendrait l'API de Stream moins fluide, avec plus de code de plaque de chaudière et par conséquent, cela ne ferait pas évoluer intrinsèquement les fonctionnalités des collections Java.

Imaginez écrire ce code avec un wrapper à chaque fois:

List<String> strings = new ArrayList<>();
...
Streams.stream(strings).filter(....);

Au Lieu de :

List<String> strings = new ArrayList<>();
...
strings.stream().filter(....);

Imaginez avec un flux enchaîné:

List<Integer> listOne = new ArrayList<>();
List<Integer> listTwo = new ArrayList<>();
...
List<int[]> values = Streams.stream(listOne)
    .flatMap(i -> Streams.stream(listTwo)
        .map(j -> new int[] { i, j }))
    .collect(Collectors.toList());

Au Lieu de

List<Integer> listOne = new ArrayList<>();
List<Integer> listTwo = new ArrayList<>();
...
List<int[]> values = listOne.stream()
    .flatMap(i -> listTwo.stream()
        .map(j -> new int[] { i, j }))
    .collect(Collectors.toList());

En particulier, ils doivent introduire le nouveau concept de Implémentation de la méthode par défaut de l'interface qui, à son tour, floue le sémantique des Interfaces vs Classes abstraites?

Cela peut être déconcertant au début mais finalement cela donne un moyen puissant de faire évoluer une API sans casser le code client en utilisant l'ancienne API.
Les méthodes par défaut ne doivent pas être considérées comme un moyen de créer des classes abstraites avec des traitements communs pour toutes les sous-classes mais comme un moyen de faites d'evolve une API sans rompre la compatibilité avec les clients des anciennes versions de l'API.

Extraire de la documentation sur les méthodes par défaut :

Les méthodes par défaut vous permettent d'ajouter de nouvelles fonctionnalités aux interfaces de vos bibliothèques et assurer la compatibilité binaire avec le code écrit pour les anciennes versions de ces interfaces.

 1
Author: davidxxx, 2017-07-17 08:58:47

Vous pouvez voir de cette façon - Si des expressions lambda ont été introduites dans jdk1.2 arrière, la façon dont l'api de collection doit avoir été conçue/implémentée sera comme la "bibliothèque de flux" ou, en d'autres termes, le flux est une collection améliorée. La plupart des codes existants utilisent des API de collecte comme source de données. Par conséquent, personne ne va utiliser stream si stream n'est pas en mesure de créer à partir d'une API de collection. Pour cette raison, ils doivent modifier l'interface existante. les méthodes 'par défaut' ont évité le rupture de toute l'interface existante et a permis d'améliorer votre code sans aucun problème.

Plus sur les "méthodes par défaut" fourni un moyen d'améliorer votre interface à l'avenir aussi. Il existe toujours d'énormes différences entre les méthodes par défaut dans les classes interface et abstract.

Il existe des méthodes dans certaines interfaces où l'implémentation sera la même dans la plupart des classes héritées. Dans ce cas, vous pouvez l'implémenter comme méthode par défaut si possible. Une fois que vous commencer à l'utiliser méthode par défaut vous allez l'adorer:)

Last but not least "Le langage devrait toujours évoluer; il ne devrait pas rester bloqué sur ce que vous avez conçu il y a 20 ans" :)

 0
Author: nantitv, 2017-07-19 21:52:08