Замыкания java.util.function.Predicate#and и Groovy 2.2

У меня есть этот код Java 8, который отлично работает:

//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)));
}

Самое близкое, что я могу сделать в Groovy (2.2):

//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)))
}

Код Groovy дает сбой со следующим в строке, которая вызывает метод and:

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

Если я заменю утверждение только на assert(is.stream().allMatch(p1)), тест завершится успешно. Проблема заключается в вызове метода and для предиката.

Проверяя, например, p2 в отладчике, я вижу, что он имеет тип OneParameterTest$_test_closure2. Декомпиляция байт-кода подтверждает это.

У меня есть ощущение, хотя я и не уверен, что это связано с неявным приведением замыкания (см. http://groovy.codehaus.org/Groovy+2.2+release+notes).

Есть ли способ написать код Groovy, чтобы он создавал предикат как истинный экземпляр java.util.function.Predicate?


person Ant Kutschera    schedule 29.04.2014    source источник
comment
Будет ли заводная версия работать, если вы напишете allMatch(p3)?   -  person Stuart Marks    schedule 30.04.2014
comment
Два небольших комментария к вашему вопросу: 1) вы никогда не используете p3, 2) в Java 8 у вас есть p1.and(p2), в Groovy у вас есть p2.and(p1) в p3, обратите внимание на обратный порядок.   -  person skiwi    schedule 30.04.2014
comment
хорошие моменты, но ни одна из них не является проблемой. вызов и на предикат проблема.   -  person Ant Kutschera    schedule 30.04.2014
comment
Пробовал с Groovy 2.3 (который поддерживает Java 8) и получил тот же результат. Может быть, опубликовать это в списке рассылки groovy-user?   -  person tim_yates    schedule 30.04.2014
comment
Хорошо, я разместил это - посмотрим, получу ли я ответы...   -  person Ant Kutschera    schedule 03.05.2014
comment
groovy.329449.n5.nabble.com/   -  person Ant Kutschera    schedule 03.05.2014


Ответы (2)


На практике проблема заключается в том, что более ранние выпуски Groovy 2.3 игнорируют методы по умолчанию, определенные в интерфейсах.

Из примечаний к выпуску Groovy 2.3:

Groovy 2.3 не поддерживает новые синтаксические конструкции, предлагаемые Java 8 (например, лямбда-выражения, ссылки на методы, методы по умолчанию в интерфейсах и т. д.), но вы вполне можете использовать новые API, предлагаемые JDK 8, и даже использовать замыкания Groovy. вместо лямбда-выражений Java 8.

Это исправлено (по крайней мере, для приведенного выше случая) в 2.3.8 и 2.4 как часть GROOVY-7104.

person ddimitrov    schedule 14.01.2015

Есть ли способ написать код Groovy, чтобы он создавал предикат как истинный экземпляр java.util.function.Predicate?

То, что вы делаете, создаете настоящий экземпляр Predicate. Проблема в том, что то, как вы их создаете, действительно полезно только для интерфейсов с одним методом. Predicate определяет несколько методов. Есть несколько способов сделать это. Возможно, проще всего воспользоваться картой Groovy для поддержки интерфейса. В приведенном ниже коде учитываются только «и» и «тест», но вы можете указать все, что вы на самом деле вызываете, и опустить другие.

Из вашего фрагмента кода трудно сказать, чего вы действительно пытаетесь достичь, но просто для того, чтобы обратиться к конкретному тесту, который вы написали, вы должны быть в состоянии сделать что-то вроде этого. Все тестовые методы жестко закодированы так, чтобы возвращать true здесь, как и в вашем тесте, но у вас, вероятно, будет настоящая логика.

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)))
person Jeff Scott Brown    schedule 11.05.2014
comment
Я понимаю, что этот пример на самом деле не делает ничего полезного, но он делает то, что пытается сделать тест в исходном посте. Может быть, вы сможете понять это отсюда, или, если вы сможете лучше описать реальную проблему, я могу показать вам решение. Во всяком случае, я надеюсь, что это поможет. - person Jeff Scott Brown; 11.05.2014