web-dev-qa-db-fra.com

Pourquoi les caractères de type fournisseur des méthodes facultatives ou et flatMap sont-ils des caractères génériques?

Le Optional.or la méthode a été ajoutée dans Java 9. C'est la signature de la méthode

public Optional<T> or​(Supplier<? extends Optional<? extends T>> supplier)

Pourquoi le paramètre type de Supplier prend-il ? extends Optional plutôt que simplement Optional, puisque Optional est une classe finale ?

Il en va de même pour le Optional.flatMap méthode. Ceci est un changement de Java 8.

Dans Java 8, c'était Function<? super T, Optional<U>> mapper alors qu'il a été remplacé par Function<? super T,​? extends Optional<? extends U>> in Java 9.

29
user7

J'ai trouvé le raisonnement derrière cela de Stuart Marks lui-même

http://mail.openjdk.Java.net/pipermail/core-libs-dev/2016-October/044026.html

Cela concerne les génériques imbriqués (Optional est imbriqué dans Function). À partir du fil de discussion

 Function<..., Optional<StringBuilder>>

n'est pas un sous-type de

 Function<..., Optional<? extends CharSequence>>

Pour contourner cela, nous devons également ajouter le caractère générique externe, afin que

 Function<..., Optional<StringBuilder>>

est un sous-type de

 Function<..., ? extends Optional<? extends CharSequence>>
35
user7

FWIW, un problème similaire avec des arguments covariants existe toujours dans Stream.iterate et Stream.iterate dans Java 11. Les signatures de méthode actuelles sont

static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

Ces signatures ne permettent pas certaines combinaisons de graines et de UnaryOperator qui sont saines du point de vue du type, par exemple ce qui suit ne se compile pas:

UnaryOperator<String> op = s -> s; 
Stream<CharSequence> scs = iterate("", op); // error

La solution proposée consiste à remplacer les signatures de méthode par

static <T, S extends T> Stream<T> iterate(S seed, Predicate<? super S> hasNext, UnaryOperator<S> next)
static <T, S extends T> Stream<T> iterate(S seed, UnaryOperator<S> f)

Donc, contrairement à Optional.or et Optional.flatMap c'est un cas où "l'approche de type de paramètre supplémentaire" fonctionne réellement.

10
Stefan Zobel

Ouais ... on dit que le caractère générique avec une extension-borne (borne supérieure) rend le type covariant, ce qui signifie que par exemple List<Apple> est un sous-type réel de List<? extends Fruit> (considérant que Apple étend Fruit); cela s'appelle aussi covariance.

Ou dans les exemples que vous avez montrés, cela signifie que Optional<StringBuilder> est un sous-type de Optional<? extends Optional<? extends CharSequence>>, vous pouvez par exemple faire:

List<Optional<String>> left = new ArrayList<>();
List<? extends Optional<? extends CharSequence>> right = new ArrayList<>();

right = left; // will compile

ou attribuez un Function à l'autre

9
Eugene