3

In Java 8, the class java.util.Optional (javadoc) class offers functionality of the "Maybe" Monad or the Option Type.

More directly:

public final class java.util.Optional<T> extends Object

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

One of the methods is:

<U> Optional<U> map(Function<? super T,? extends U> mapper)

If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result.

The question is simply why does map() use a type wildcard on the U type.

My understanding of type wildcarding is simply that:

enter image description here

? super T designates some type from the set of classes given by the subclassing path from Object to T (both Object and T included) union the set of interfaces found on any subinterfacing path pulled in "as a sidedish" via implements.

And ? extends U simply designates some type from the set of classes that extend U (Uincluded).

So one could just write:

<U> Optional<U> map(Function<? super T,U> mapper)

without loss of information.

Or not?

David Tonhofer
  • 14,559
  • 5
  • 55
  • 51
  • Pretty close question (if not identical): [Whats the use of saying extends SomeObject> instead of ](https://stackoverflow.com/questions/17834145/whats-the-use-of-saying-extends-someobject-instead-of-someobject/) – David Tonhofer Aug 15 '17 at 21:30

1 Answers1

7

Not quite.

A Function<? super T, U> is not the same as a Function<? super T, ? extends U>.

For example, I could still get an Optional<CharSequence>, even if I passed a Function<Object, String> to the method. If the method was defined as <U> Optional<U> map(Function<? super T, U> mapper), then this would not be possible.

That is because generics are invariant: <T> is not the same as <? extends T>. That's a design decision implemented in the Java language.

Let's see how Jon Skeet explains what would happen if generics would not be invariant:

class Animal { }

class Dog extends Animal { }

class Cat extends Animal { }
public void ouch() {
    List<Dog> dogs = Arrays.asList(new Dog(), new Dog());
    List<Animal> animals;
    // This would be legal, right? Because a list of dogs is a list of animals.
    List<Animal> animals = dogs;
    // This would be legal, right? Because a cat could be added to a
    // list of animals, because a cat is an animal.
    animals.add(new Cat());
    // Unfortunately, we have a confused cat.
}

Although I'm not entirely sure what you mean in your comments, I'll try to elaborate.

If you have full control over the Function you provide, it doesn't matter whether the method's signature is Function<? super T, U> or Function<? super T, ? extends U>, you'll just adapt your Function accordingly. But the authors of the method probably wanted the method to be as flexible as possible, allowing one to provide a Function with its second parameter to be also a subclass of U, instead of only U itself. Actually you widen its lower bound from U to a certain subtype of U.

So the function should really read <U> Optional<? the-most-general-but-fixed-supertype-of U> map(Function<? super T, U> mapper) but expressing it that way would be awkward.

I would be indeed awkward. In addition, there is a difference between your proposed notation and the actual method signature of map(), which involve implications of lower bounds and upper bounds.

Read more:

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • 1
    `Optional.map` signature is a good example of [Producer Extends Consumer Super](https://books.google.ru/books?id=ka2VUBqHiWkC&pg=PA136&lpg=PA136&dq=bloch+effective+java+pecs+mnemonic&source=bl&ots=yYKnPjt-P-&sig=JGT8qexAAldJ5xYPepbBQ5uude0&hl=en&sa=X&ei=Cb3GUv-sNIuK5ATM7oD4Dw&ved=0CCgQ6AEwAA#v=onepage&q=bloch%20effective%20java%20pecs%20mnemonic&f=false) principle – Maxim Ponomarev Aug 15 '17 at 16:06
  • Let me think about this for a minute. _Invariant_ means that generic types are not comparable or in any hierarchical relationship, right? Because that would open a can of worms or run-time type information carry & checks. Whereas using wildcards allows us to express an additional constraint holding between related types. – David Tonhofer Aug 15 '17 at 16:42
  • It feel like it's a question of intent. We want to express the following: "map is a function that will return `Optional` where `U` is a most generic type that still makes sense here (e.g. `CharSequence`, and because we want to avoid writing type-specific code for example), and it accepts a `mapper` from which we just require that it return some subtype of `U`. The point being that `mapper`s may already exist and we want to accomodate them all while coding `map()` for them. As opposed to thinking the other way: `map()` already exists and we code the `mapper`s that we give it... – David Tonhofer Aug 15 '17 at 17:01
  • ...in which case the restrictive ` Optional map(Function super T,U> mapper)` would be fine. So the function should really read ` Optional the-most-general-but-fixed-supertype-of U> map(Function super T,U> mapper)` but expressing it that way would be awkward. – David Tonhofer Aug 15 '17 at 17:08
  • I will accept this as answer because it helped, though its was not exactly what I had in mind. – David Tonhofer Aug 15 '17 at 21:23
  • I tried to elaborate on the things you commented, I updated my post. – MC Emperor Aug 16 '17 at 07:28