Choice of wildcard in an API design case

45 views
Skip to first unread message

zhong...@gmail.com

unread,
Jul 27, 2015, 4:38:45 PM7/27/15
to java.lang.fans
from http://stackoverflow.com/a/28520504/2158288

This method definition is not flexible enough -

#1   void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier)

alternative choices -

#2   <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier)

#3 <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier)

#4 <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier)

My comments -

#2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable

#4 is interesting. for type inference, A has a range of choices. It might be confusing to the programmer, regardless of how spec/compiler behaves. It gets more confusing when the arguments are implicit lambda expressions. Also very confusing if consumer is Consumer<Runnable>, since the programmer may not be able to follow the exact inference rules.

#3 seems to be the best choice. A is easily fixed; nothing confusing/difficult to the programmer. The method can be considered an extended instance method on Supplier<A extends Animal>, so the lack of wildcard is justified. This case might be an evidence that wildcard is not always desirable on a naturally covariant type.

Zhong Yu
bayou.io

Zhong Yu

unread,
Jul 27, 2015, 5:52:47 PM7/27/15
to java.lang.fans
Thoughts -

If a method type parameter appears as the bound of a wildcard, it's preferable that there exists other constraints that can fix the type variable during inference. We don't want a "floating" type variable with a wider range of choices; it's a little hard to understand.

If a method argument is likely an implicit lambda expression, it does not provide much constraint during inference; the parameter type is a good candidate for containing wildcards.

If all method arguments are likely implicit lambda expressions, use the return type for fixed constraint, because the method invocation must have a target type. For example, Function.compose() returns Function<V,R> without wildcards. The argument to the method is likely an implicit lambda; the invocation likely has a target type that'll fix <V>.

Zhong Yu
bayou.io

y s

unread,
Jul 28, 2015, 10:46:06 AM7/28/15
to Zhong Yu, java.lang.fans
Why do you even need a named type parameter at all? You're not using it in the return argument or anything.

#5   void passOn(Consumer<? super Animal> consumer, Supplier<? extends Animal> supplier)
In the body of the method, rather than getting an A from the supplier and passing it to the consumer, you just get an Animal from the supplier and pass it to the consumer.
I don't think you gain anything by declaring the "A" parameter, since the compiler can always infer A to Animal anyway, turning #4 into the equivalent of #5.

--
You received this message because you are subscribed to the Google Groups "java.lang.fans" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-lang-fan...@googlegroups.com.
To post to this group, send email to java-la...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/java-lang-fans/CACuKZqG8z%3DQYp6d3mN%2B3aaxkW8ZC%3D3Oi5nNZ_6G_S7cBVnTdSA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Zhong Yu

unread,
Jul 28, 2015, 3:55:40 PM7/28/15
to y s, java.lang.fans
On Tue, Jul 28, 2015 at 9:45 AM, y s <eleus...@gmail.com> wrote:
Why do you even need a named type parameter at all? You're not using it in the return argument or anything.

#5   void passOn(Consumer<? super Animal> consumer, Supplier<? extends Animal> supplier)
In the body of the method, rather than getting an A from the supplier and passing it to the consumer, you just get an Animal from the supplier and pass it to the consumer.
I don't think you gain anything by declaring the "A" parameter, since the compiler can always infer A to Animal anyway, turning #4 into the equivalent of #5.


#5 won't work on (Consumer<Dog>, Supplier<Dog>) for example.

The idea is to accept (Consumer<X>, Supplier<Y>) where Y<:X and Y<:Animal.

Zhong Yu
 

zhong...@gmail.com

unread,
Sep 17, 2015, 10:27:36 AM9/17/15
to java.lang.fans
Similar cases: Collections min/max()

     <T>
     T min(Collection<? extends T> coll, Comparator<? super T> comp)

     <T extends Comparable<? super T>>
     T min(Collection<? extends T> coll)

The methods can simply accept `Collection<T> coll`, without wildcards.

However, people may prefer to use wildcard consistently, as a matter of principle.

Zhong
Reply all
Reply to author
Forward
0 new messages