Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 820
snr Avatar asked Aug 10 '18 12:08

snr


3 Answers

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<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.

like image 52
Eugene Avatar answered Oct 09 '22 08:10

Eugene


In:

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

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

While in:

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

<String> is a type argument to a 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.

like image 23
Oleksandr Pyrohov Avatar answered Oct 09 '22 09:10

Oleksandr Pyrohov


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.

like image 4
Jacob G. Avatar answered Oct 09 '22 07:10

Jacob G.