Using wildcards in stream terminal operations - java

Hi2all.
I'am reading a Java book and got to the chapter about StreamAPI classes.
So, my questions is: why in terminal operation methods using lower bounded wildcards, like:
void forEach(Consumer<? super T> consumer)
boolean anyMatch(Predicate <? super T> pred)
Optional<T> min(<? super T> comparator)
Indeed, in fact, you can use unbounded wildcards like this:
void forEach(Consumer<?> consumer)
boolean anyMatch(Predicate <?> pred)
Optional<T> min(<?> comparator)
Is there any reason to use exactly lower- bounded wildcards?

You cannot use unbounded wildcards for these methods.
Suppose you are implementing your own forEach method. If it accepts a Consumer<? super T>, you can pass a T to it, because you know that T is a subtype of whatever the Consumer's parameter's type is and can therefore safely make the consumer accept an element of type T.
However, if it accepts a Consumer<?>, you can't directly pass a T to it without casting. There is no guarantee that the consumer accepts Ts and not some other type? Furthermore, as Louis Wasserman pointed out in his answer, you would be able to pass a Consumer<Long> to the forEach method of a Stream<String>.
Lower-bounded wildcards are as loose as you can get while also being typesafe. With just plain Consumer<T> and Supplier<T> parameters, you wouldn't be able to directly pass a Consumer<Object> to a Stream<String>'s forEach method without casting. With unbounded wildcards, you would be able to pass basically anything, which defeats the whole point of using generics.

You're wrong in saying that you can use "unbounded wildcards." You can't, and the terminal operations you describe have the most permissive types possible.
Consumer<Long> longConsumer = (l) -> System.out.println(l * 2);
Stream<String> stringStream = Stream.of("a", "b");
stringStream.forEach(longConsumer);
// should not work, but would work if forEach accepted Consumer<?>

Related

Why Comparable and Comparator are consumers in PECS wildcard types in Java

In Effective Java, in the item "Use bounded wildcards to increase API flexibility", when talking about the usage of PECS (producer-extends, consumer-super), the author mentioned that:
Comparables are always consumers, so you should generally use Comparable<? super T> in preference to Comparable. The same is true of comparators; therefore, you should generally use Comparator<? super T> in preference to Comparator.
It is not clear to me why Comparables and Comparators are considered consumers.
In one of the topic discussing PECS, What is PECS (Producer Extends Consumer Super)?, the consumer is usually referring to a Collection as a parameter for some generic method.
While here Comparable is just an interface.
Can anyone share some insights? Thanks!
A nice analogy can be drawn to the interfaces Consumer<T> and Supplier<T> (with Supplier being analogous to Producer). A Consumer<T> is a function that takes in a T, while a Supplier<T> is a function that returns a T. Notice that we are talking about method signatures and return type, we say nothing about the semantics of the method. This is a core property of PECS: it is independent of the semantics and can be determined solely on the signature and return type of the methods used.
Looking at Comparable<T> and Comparator<T>, we find that both have methods (int compareTo(T) and int compare(T, T)) that take in, i.e. consume, T's.
For the collections, we have to look on how we use the collection, i.e. if we are using producer- or consumer-methods:
If we retrieve data from the collection (T get(int), iterator, ...), the list produces values for us and we use ? extends T.
If we use the collection to store data, (i.e. we call add(T), addAll(Collection<T>), contains(T), ...), we call consuming methods, thus the method is a consumer of our data and we use ? super T.
If we use a collection to both store and retrieve values, the collection acts as a consumer and producer at the same time, thus we have to use the precise T, neither using ... extends ... nor ... super ....
„…It is not clear to me why Comparables and Comparators are considered consumers.…“
Anytime a method member of a generic class C<T>, takes in (i.e. „consumes“) an argument that is of type T, then that method is a consumer of Ts.
So Comparator<T>.compareTo(T o) is said to be a „consumer“ of the o object of the type represented by the type variable T.
When we say "Comparable is a consumer", we actually mean "the method of the interface is a consumer". This is an answer to your doubt that "Comparable is just an interface."
See its signature: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Comparable.html#compareTo(T)
int compareTo​(T o)
It consumes the generic T, it does not produce any generic object.

Kotlin Generics declaration-site variance <in T> construction

I was reading about reasons why kotlin does not have wildcards (https://kotlinlang.org/docs/reference/generics.html). It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards. I think I understood how <out T> works but I have troubles with <in T>. So in java we could write something like this:
public List<? extends Number> list1;
public List<? super String> list2;
First case after initialization becomes read only list (though not perfectly immutable cause we could clear it) which could be read if we treat every element as Number.
Second case is write only (though we could read it if we treat every element as Object). We could write there String and it subclasses.
In Kotlin I was able to recreate list1 example using <out T> like this:
class Service {
val container = Container(mutableListOf("1", "2", "3"))
}
class Container<T>(var list1: MutableList<out T>)
Finally I tried something similar with <in T> thinking that I could recreate list2 example, but I failed:
Can someone explain to me how to achieve my list2 example in Kotlin? How should I use <in T> construction in proper way?
Kotlin List<E> is not equivalent to Java List<E>. The Java list has the mutating functions, while the Kotlin list is read-only. It's Kotlin MutableList<E> that is equivalent to the Java list.
Next, take a look at the List<E> declaration: its type parameter is covariant (out E), and the declaration-site variance cannot be overridden by use-site variance, that's why you cannot have a List<in T>.
Moreover, the declaration-site variance out E means that E never appears in an in-position (there's no function parameter of type E and no mutable property of type E), and indeed, since List<E> is read-only, it doesn't take E into any of its functions (*).
You can convert your example to use MutableList<E> instead:
class Container2<T>(var list2: MutableList<in T>)
The MutableList<E> interface has its E invariant, and the in-projection at use-site does not conflict with declaration-site variance.
(*) Actually, it does, but only using the parameters that are marked with the #UnsafeVariance annotation, which simply suppresses the variance conflict, more about it can be found here.
Also, a small remark:
It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards.
It's actually use-site variance that replaces Java wildcards. Declaration-site variance is only applied at the type parameter declaration (where the type is defined), and when it is, all the usages of the type will have that variance (e.g. when you use List<CharSequence>, it's actually a List<out CharSequence> because of the declaration-site variance of List<out E>). And use-site variance is, accordingly, for particular usages of a generic type.

Inconsistency in Java 8 method signatures

Java 8 has given us new methods with really long signatures like this:
static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(
Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
What I find odd about this is that wildcards have been used to ensure that the first two parameters are as general as possible, yet the third parameter is just a BinaryOperator<U>. If they'd been consistent, surely it would be a BiFunction<? super U,? super U,? extends U>?. Am I missing something? Is there a good reason for this, or did they just want to avoid making an already horrendous signature even worse?
Edit
I understand PECS, and I understand the principle that mergeFunction should be thought of as a way of taking two Us and getting back a U. However it would be useful to be able to have an object that could be reused in many different ways. For example:
static final BiFunction<Number, Number, Double>
MULTIPLY_DOUBLES = (a, b) -> a.doubleValue() * b.doubleValue();
Obviously this is not a BinaryOperator<Double>, but it could be treated as one. It would be great if you could use MULTIPLY_DOUBLES as both a BiFunction<Number, Number, Double> and a BinaryOperator<Double>, depending on the context. In particular, you could simply pass MULTIPLY_DOUBLES to indicate that you want a load of doubles to be reduced using multiplication. However the signature for toMap (and other new methods in Java 8) does not allow for this kind of flexibility.
You are right in that the functional signature of the merge operation (the same applies to reduce) does not require an interface like BinaryOperator.
This can not only be illustrated by the fact that the mergeFunction of the toMap collector will end up at Map.merge which accepts a BiFunction<? super V,? super V,? extends V>; you can also convert such a BiFunction to the required BinaryOperator:
BiFunction<Number, Number, Double>
MULTIPLY_DOUBLES = (a, b) -> a.doubleValue() * b.doubleValue();
Stream<Double> s = Stream.of(42.0, 0.815);
Optional<Double> n=s.reduce(MULTIPLY_DOUBLES::apply);
or full generic:
public static <T> Optional<T> reduce(
Stream<T> s, BiFunction<? super T, ? super T, ? extends T> f) {
return s.reduce(f::apply);
}
The most likely reason for creating BinaryOperator and UnaryOperator is to have symmetry with the primitive type versions of these functions which don’t have such a super interface.
In this regard, the methods are consistent
Stream.reduce(BinaryOperator<T>)
IntStream.reduce(IntBinaryOperator)
DoubleStream.reduce(DoubleBinaryOperator)
LongStream.reduce(LongBinaryOperator)
or
Arrays.parallelPrefix(T[] array, BinaryOperator<T> op)
Arrays.parallelPrefix(int[] array, IntBinaryOperator op)
Arrays.parallelPrefix(double[] array, DoubleBinaryOperator op)
Arrays.parallelPrefix(long[] array, LongBinaryOperator op)
the BinaryOperator<U> mergeFunction needs to take Us from an input source and put them into another consumer.
Due to the Get and Put Principle, the type has to be exactly the same. No wild cards.
The get-put principle, as stated in Naftalin and Wadler's fine book on generics, Java Generics and Collections says:
Use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you do both.
Therefore it can't beBiFunction<? super U,? super U,? extends U> mergefunction because we are doing get and put operations. Therefore the input and result type must be identical.
see these other links for more about Get and Put:
Explanation of the get-put principle (SO question)
http://www.ibm.com/developerworks/library/j-jtp07018/
EDIT
As Gab points out, the Get and Put principle is also known by the Acronym PECS for "Producer Extends Consumer Super"
What is PECS (Producer Extends Consumer Super)?
Looking at the implementation of the Collectors#toMap in question, one can see that the operator is passed through to some other methods, but eventually only arrives as the remappingFunction in various forms of Map#merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction).
So using BiFunction<? super V, ? super V, ? extends V> instead of BinaryOperator<V> would indeed work here, without causing any problem. But not only here: The BinaryOperator is only a specialization of BiFunction for the case that the operands and the result are all of the same type. So there are many places where one could allow passing in a BiFunction<? super V, ? super V, ? extends V> instead of a BinaryOperator<V> (or, more obviously: One could always use a BiFunction<V, V, V> instead...)
So up to this point, there seems to be no technical reason why they chose to only support a BinaryOperator<U>.
There was already speculation about possible non-technical reasons. For example, limiting the complexity of the method signature. I'm not sure whether this applies here, but it could, indeed, be a trade-off between the complexity of the method and the intended application cases: The concept of a "binary operator" is easily comprehensible, for example, by drawing analogies to a simple addition or the union of two sets - or maps, in this case.
A possible not-so-obvious technical reason could be that there should be the possibility to provide implementations of this method that internally would not be able to cope with the BiFunction. But considering that the BinaryOperator is only a specialization, it's hard to imagine what such an implementation should look like.

Java Generics - List<?> vs List<T>

Consider the following 2 alternate APIs:
void method(List<?> list)
<T> void method(List<T> list)
I know that their internal implementation will have many differences to deal with, such as List<?> wont be able to write into the list etc.
Also, in my knowledge List<?> will allow any parameterized type with List as base type. So will be the case with List<T> also.
Can anybody tell me if there is any difference at all in what kinds of inputs these 2 APIs will accept. (NOT the 2 APIs internal implementation differences.)
The internal implementation is exactly the same. In fact, both methods compiled with javac will yield equal method byte code, if they compile at all).
However, during compilation, the first method is specified to not care about the component type of the list, while the second requires that the component type be invariant. This means that whenever I call such a method, the component type of list will be fixed by whatever the call site uses.
I can call my method with a List<String> and T will be synonymous to String during the call (from the perspective of the compiler). I can also call it with a List<Runnable> and T will be synonymous to Runnable during the call.
Note that your method does not return anything, but it very well could do so depending on the arguments. Consider the method:
<T> T findFirst(Collection<T> ts, Predicate<T> p) { … }
You can use this method for each T. BUT it only works if our T is equal for the collection and predicate — this is what "invariance" means. You could in fact specify the method to be applicable in more contexts:
<T> T findFirst(Collection<? extends T> ts, Predicate<? super T> p) { … }
This method would work the same as above, but it would be more lenient in what types it accepts. Consider a type hierarchy A extends B extends C. Then you could call:
Collection<A> cs = …;
Predicate<C> p = …;
B b = findFirst(cs, p);
We call the type of ts covariant and the type of p (in the method signature) contravariant.
Wildcards (?) are a different matter. They can be bounded (like in our cases above) to be co- or contravariant. If they are unbounded, the compiler actually needs a concrete type to fill in at compile time (which is why you will sometimes get errors like "type wildcard-#15 is not a match for wildcard-#17"). The specific rules are laid out in the Java Language Specification, Section 4.5.1.

generic methods and wildcards

What are the differences between the following three signatures?
static <T> void foo(List<T>, Comparator<? super T>);
static <T> void bar(List<? extends T>, Comparator<T> );
static <T> void baz(List<? extends T>, Comparator<? super T>);
I know what extends and super mean in Generics. My question is whether there is a difference between foo, bar and baz. Should I make one of the parameters invariant and the other one variant in the appropriate direction, or should I make both of them variant? Does it make a difference?
PECS - Producer extends, consumer super.
To explain this "rule":
extends means the genericized object produces elements of the type. When it's a collection, it means you can only take elements from the collection, but not put them in. The comparator
super means the object consumes objects of the selected type. So you can add to the collection, but not read from it.
the lack of extends and super means you can do both for the exact type specified.
Regarding the Comparator, I don't think it makes any difference. Normally, it would be <? super T> because you the comparator consumes objects, but in all three cases you can safely invoke Collections.sort(list, comparator); (the signature of which is <? super T>)
The only difference is whether T represents the type parameter of the List, the Comparator, or something in between.
As far as the caller is concerned, the three method signatures are equivalent, i.e. whenever one of them can be used, the others can be used as well.
For the method implementation foo is probably most convenient, as it allows modifying the list without needing an additional capture conversion, which would require delegating to a helper method.
I believe that ? extends T means that the List may be generic of any type that is derived from T, whereas List<T> can only be a List of T and not any of its derived classes.

Categories

Resources