Generic method reference type specifying before/after :: operator

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Generic method reference type specifying before/after :: operator



What is the difference between the following method references,


BiPredicate<List<String>,String> contains1 = List<String>::contains;

BiPredicate<List<String>,String> contains2 = List::<String>contains;

BiPredicate<List<String>,String> contains3 = List<String>::<String>contains;



Do the cases have special names? Is there any example similar to the usage?





Good question...
– ernest_k
Aug 10 at 12:35





Related: stackoverflow.com/questions/31245127/…. Seems the first syntax specifies type argument for List, whereas the second specifies type argument for contains (unnecessary in this case because the method is not generic)
– ernest_k
Aug 10 at 12:44


List


contains





And, of course, BiPredicate<List<String>,String> contains1 = List<String>::<String>contains;.
– Andy Turner
Aug 10 at 12:45


BiPredicate<List<String>,String> contains1 = List<String>::<String>contains;





As a related side-note, and a partial explanation: it's legal to supply type arguments to a non-generic method, such as list.<Number>contains("foo"). They're just ignored. (As for why the JLS authors chose to allow it, though, I don't know.)
– Radiodef
Aug 10 at 16:53



list.<Number>contains("foo")





@snr Why is it legal to supply type arguments to a non-generic method? You can find the answer here.
– Oleksandr
Aug 10 at 20:03





3 Answers
3



First of all, that is called a type witness (in the official Oracle Tutorial) or TypeArguments (in the JLS Sec 15.12) and you are effectively helping the compiler with such constructs.



One example:


private static void test(Callable<Object> call)



private static void test(Runnable run)



static class Gen<T>




And call it via test(Gen::new); (this will fail, never mind why), but the point is that you add a type witness to help the compiler, so this would work


test(Gen::new);


test(Gen<String>::new);



So when you write List<String>, you have added a type witness for the target type - List that is; in the second case you are adding one for the method contains - but it's not generic, so it is ignored.


List<String>


List


contains





Where does the term "type witness" come from? I can't find it in the language spec.
– Andy Turner
Aug 10 at 12:50





@AndyTurner docs.oracle.com/javase/tutorial/java/generics/… search for "type witness", I was not saying it's in the JLS (might be, not sure)
– Eugene
Aug 10 at 12:51


JLS





@Lino the official java tutorial calls it that way, I'll stick to that :)
– Eugene
Aug 10 at 12:56





The word "witness" doesn't appear in JLS (9, at least). They are simply called TypeArguments there.
– Andy Turner
Aug 10 at 12:58



TypeArguments





@AndyTurner TypeArguments it is! thank you
– Eugene
Aug 10 at 13:00


TypeArguments



In:


BiPredicate<List<String>, String> contains2 = List::<String>contains;



<String> is a type argument to a non-generic List.contains method1.


<String>


List.contains



While in:


BiPredicate<List<String>, String> contains1 = List<String>::contains;



<String> is a type argument to a List.


<String>


List



1 - In this particular case a type argument is ignored according to the JLS §15.12.2.1:



A non-generic method may be potentially applicable to an invocation
that supplies explicit type arguments. In such a case, the type
arguments will simply be ignored.





Is there any difference btw type parameter and type argument terms in this context? If yes, what is this?
– snr
Aug 10 at 18:41





Yes, there is a difference. E in List<E> is a type parameter and the String in List<String> is a type argument. You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you are passing a type argument - String in this case - to the List itself (source).
– Oleksandr
Aug 10 at 19:09



E


List<E>


String


List<String>


String


List





thank you indeed
– snr
Aug 10 at 22:10



Here's what Intellij tells me about them:


BiPredicate<List<String>, String> contains1 = List<String>::contains;



Explicit type arguments can be inferred


BiPredicate<List<String>, String> contains2 = List::<String>contains;



Type arguments are redundant for the non-generic method reference



If you were to split these up into their respective lambda functions, I believe you'd see the following:


BiPredicate<List<String>, String> contains1 = (List<String> strings, String o) -> strings.contains(o);
BiPredicate<List<String>, String> contains2 = (strings, o) -> strings.<String>contains(o);



As we know, (List<String> strings, String o) can be replaced by (strings, o) and <String> on the second line is unneeded (as String#contains isn't generic), so it's safe to assume that both method references are equivalent.


(List<String> strings, String o)


(strings, o)


<String>


String#contains






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard