Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.util.function.Predicate#and and Groovy 2.2 closures

I have this Java 8 code which works fine:

//Java 8
@Test public void testPredicates(){
    Predicate<Integer> p1 =  (i) -> true;
    Predicate<Integer> p2 =  (i) -> true;
    Predicate<Integer> p3 =  p1.and(p2);
    List<Integer> is = new ArrayList<>();
    is.add(1);
    is.add(2);
    assertTrue(is.stream().allMatch(p1.and(p2)));
}

The closest I can get to it in Groovy (2.2) is this:

//Groovy 2.2
@Test
void test(){
    Predicate<Integer> p1 = { i -> true}
    Predicate<Integer> p2 = {i -> true}
    Predicate<Integer> p3 = p2.and(p1)
    List<Integer> is = new ArrayList<>()
    is.add(1)
    is.add(2)
    assert(is.stream().allMatch(p1.and(p2)))
}

The Groovy code fails with the following on the line which calls the and method:

java.lang.ClassCastException: java.lang.Boolean 
    cannot be cast to java.util.function.Predicate

If I replace the assertion with just assert(is.stream().allMatch(p1)), then the test completes successfully. The problem is calling the and method on the predicate.

Inspecting for example p2 in the debugger, I can see it has type OneParameterTest$_test_closure2. Decompiling the bytecode verifys this.

I have a feeling, although I am not sure, that this relates to implicit closure coersion (see http://groovy.codehaus.org/Groovy+2.2+release+notes).

Is there any way to write Groovy code so that it creates the predicate as a true instance of java.util.function.Predicate?

like image 611
Ant Kutschera Avatar asked Oct 21 '22 09:10

Ant Kutschera


2 Answers

In practice, the problem is that the earlier releases of Groovy 2.3 ignore the default methods defined in the interfaces.

From Groovy 2.3 release notes:

Groovy 2.3 doesn’t support the new syntax constructs offered by Java 8 (such as lambdas, method references, default methods in interfaces, etc), but you can very well already use the new APIs offered by JDK 8, and even use Groovy closures in lieu of Java 8 lambdas.

It is fixed (at least for the above case) in 2.3.8 and 2.4 as part of GROOVY-7104

like image 181
ddimitrov Avatar answered Oct 23 '22 00:10

ddimitrov


Is there any way to write Groovy code so that it creates the predicate as a true instance of java.util.function.Predicate?

What you are doing there is creating a true instance of Predicate. The problem is the way you are creating them is really only useful for single method interfaces. Predicate defines several methods. There are several ways you could go about this. Maybe the simplest is to take advantage of Groovy's Map to interface support. The code below only accounts for "and" and "test", but you can provide whatever you actually are calling and omit the others.

It is hard to say from your code snippet what you are really trying to accomplish but just to address the specific test you have written, you should be able to do something like this. The test methods are all hardcoded to return true here, just like in your test, but you presumably would have real logic there.

Predicate<Integer> p1 = [test:{ t -> true},
                         and: { Predicate other -> [test: { t -> true}] as Predicate}] as Predicate

// in your example you are never calling p2.and(...), but
// it is included here for consistency with the one above...
Predicate<Integer> p2 = [test:{ t -> true},
                         and: { Predicate other -> [test: { t -> true}] as Predicate}] as Predicate

// it isn't clear why you are creating p3, but you can...
Predicate<Integer> p3 = p1.and(p2)
List<Integer> is = new ArrayList<>()
is.add(1)
is.add(2)
assert(is.stream().allMatch(p1.and(p2)))
like image 38
Jeff Scott Brown Avatar answered Oct 23 '22 00:10

Jeff Scott Brown