简体   繁体   中英

Why is "? extends" required here in the type declaration

I noticed that this Java 17 code snippet doesn't compile.

 class Foo<T> { public Optional<T> getSomeOptional() { return Optional.empty(); } } class Bar { List<Foo<?>> fooList = new ArrayList<>(); void bar() { List<Optional<?>> list = fooList.stream().map(Foo::getSomeOptional).toList(); } }

but changing class Bar to this works.

 class Bar { List<Foo<?>> fooList = new ArrayList<>(); void bar() { List<? extends Optional<?>> list = fooList.stream().map(Foo::getSomeOptional).toList(); } }

Why is ? extends ? extends required here in the declaration?

Compilers don't always infer types of complex expressions correctly. In this case, it may help to break them into parts. Eg in your case, this works:

void bar() {
    Stream<Optional<?>> stream = fooList.stream().map(Foo::getSomeOptional);
    List<Optional<?>> list = stream.toList();
}

The type guessed by type inference (ie when you call a generic method without a type witness) is not always what you want. I think here what happened is that Foo::getSomeOptional takes a Foo<T> and returns an Optional<T> , so the compiler tries to "capture" the wildcard from Foo<?> into an unnamed type variable, so it thinks the result is Optional<some capture> . So you get List<Optional<some capture>> , rather than List<Optional<?>> . Although Optional<some capture> is a subtype of Optional<?> , List<Optional<some capture>> is not a subtype of List<Optional<?>> , since generics are invariant.

If you add the following type witness to tell it that you want Optional<?> (with the plain wildcard) as the result type, it should compile:

 List<Optional<?>> list = fooList.stream().<Optional<?>>map(Foo::getSomeOptional).toList();

This thing you have used is called wildcards.

https://www.geeksforgeeks.org/wildcards-in-java/

Because of Java Type Erasure in the Compile Time, if we don't use a type for our generics then it is considered from the object type, so we use "?" sign or wildcards, by this we basically say that if we pass something in the declaration instead of ?, the compiler should bound the generic to that and don't make it object type.

Note: if you don't bound with anything no matter what type you use, because of java Erasure it's type will become it's super class(the top one).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM