[java] 메소드 참조 술어를 부정하는 방법

Java 8에서 메소드 참조를 사용하여 스트림을 필터링 할 수 있습니다. 예를 들면 다음과 같습니다.

Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();

기존 참조의 부정 인 메소드 참조를 작성하는 방법이 있습니까?

long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

not아래와 같은 방법을 만들 수는 있지만 JDK가 비슷한 것을 제공하는지 궁금합니다.

static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }



답변

Predicate.not( … )

새로운 메소드 술어를 제공합니다

따라서 메소드 참조를 부정 할 수 있습니다.

Stream<String> s = ...;
long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count();


답변

메소드 참조를 인라인으로 사용할 수 있도록 다음을 정적 가져 오기로 계획하고 있습니다.

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

예 :

Stream<String> s = ...;
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();

업데이트 : Java-11부터 JDK는 유사한 솔루션을 기본제공합니다.


답변

현재 메소드 참조와 반대 인 메소드 참조를 작성하는 방법이 있습니다. 아래의 @vlasec의 답변을 참조하여 메소드 참조를 a로 명시 적으로 캐스팅 Predicate한 다음 negate함수를 사용하여 변환 하는 방법을 보여줍니다 . 그렇게하는 몇 가지 다른 방법 중 하나입니다.

이것의 반대 :

Stream<String> s = ...;
int emptyStrings = s.filter(String::isEmpty).count();

이것입니다 :

Stream<String> s = ...;
int notEmptyStrings = s.filter(((Predicate<String>) String::isEmpty).negate()).count()

아니면 이거:

Stream<String> s = ...;
int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count();

개인적으로, 나는 it -> !it.isEmpty()장황한 명시 적 캐스트보다 읽는 것이 더 명확하고 부정 하기 때문에 나중의 기술을 선호합니다 .

술어를 만들어 재사용 할 수도 있습니다.

Predicate<String> notEmpty = (String it) -> !it.isEmpty();

Stream<String> s = ...;
int notEmptyStrings = s.filter(notEmpty).count();

또는 컬렉션이나 배열이있는 경우 간단하고 오버 헤드가 적고 * 더 빠를 수있는 for 루프를 사용하십시오.

int notEmpty = 0;
for(String s : list) if(!s.isEmpty()) notEmpty++;

* 빠른 것이 무엇인지 알고 싶다면 JMH http://openjdk.java.net/projects/code-tools/jmh 를 사용 하고 모든 JVM 최적화를 피하지 않는 한 핸드 벤치 마크 코드를 피하십시오 ( Java 8 : Streams의 성능 참조) . vs 컬렉션

** for-loop 기술이 더 빠르다는 제안에 착각했습니다. 스트림 작성을 제거하고 다른 메소드 호출 (조건 자에 대해 음수 함수)을 사용하지 않고 임시 누적 기 목록 / 카운터를 제거합니다. 따라서 마지막 구성으로 저장되는 몇 가지 사항은 더 빠를 수 있습니다.

그래도 더 빠르지는 않더라도 더 간단하고 훌륭하다고 생각합니다. 직업이 망치와 못을 요구하는 경우 전기 톱과 접착제를 가져 오지 마십시오! 나는 당신 중 일부가 그 문제를 안다는 것을 알고 있습니다.

희망 목록 : StreamJava 사용자가 익숙해지면서 Java 기능이 조금 발전하고 싶습니다 . 예를 들어 Stream의 ‘count’메서드는 Predicate다음과 같이 직접 수행 할 수 있도록 허용 할 수 있습니다.

Stream<String> s = ...;
int notEmptyStrings = s.count(it -> !it.isEmpty());

or

List<String> list = ...;
int notEmptyStrings = lists.count(it -> !it.isEmpty());


답변

Predicate방법을 가지고 and, or그리고 negate.

그러나 String::isEmpty는 아닙니다. Predicate단지 String -> Boolean람다이며 여전히 예를 들어 무엇이든 될 수 있습니다 Function<String, Boolean>. 형식 유추 가 먼저 발생해야합니다. 이 filter메소드는 타입을 암시 적으로 유추합니다 . 그러나 인수로 전달하기 전에 부정하면 더 이상 발생하지 않습니다. @axtavt가 언급했듯이 명시 적 추론은 추악한 방법으로 사용될 수 있습니다.

s.filter(((Predicate<String>) String::isEmpty).negate()).count()

다른 답변에는 다른 방법이 있습니다. 정적 not방법과 람다는 가장 좋은 아이디어 일 것입니다. 이것으로 tl; dr 섹션을 마 칩니다 .


그러나 람다 형식 유추에 대해 더 깊이 이해하고 싶다면 예제를 사용하여 조금 더 깊이 설명하고 싶습니다. 이것들을보고 무슨 일이 일어나는지 알아 내십시오.

Object obj1                  = String::isEmpty;
Predicate<String> p1         = s -> s.isEmpty();
Function<String, Boolean> f1 = String::isEmpty;
Object obj2                  = p1;
Function<String, Boolean> f2 = (Function<String, Boolean>) obj2;
Function<String, Boolean> f3 = p1::test;
Predicate<Integer> p2        = s -> s.isEmpty();
Predicate<Integer> p3        = String::isEmpty;
  • obj1이 컴파일되지 않습니다-람다는 기능적 인터페이스를 유추해야합니다 (= 하나의 추상 메소드 사용)
  • p1과 f1은 각각 다른 유형을 유추하여 잘 작동합니다.
  • obj2보다 A는 캐스트 PredicateObject바보 만 유효 –
  • F2는 런타임에 실패하지 않습니다 – 당신이 캐스팅 할 수 PredicateFunction,이 추론에 대해 더 이상이다
  • f3 작동- test람다로 정의 된 술어의 메소드 를 호출
  • p2는 컴파일되지 않습니다- 메소드 Integer가 없습니다isEmpty
  • p3도 컴파일되지 않습니다- 인수가있는 String::isEmpty정적 메소드 가 없습니다Integer

이것이 유형 유추 작동 방식에 대한 통찰력을 얻는 데 도움이되기를 바랍니다.


답변

다른 사람의 답변과 개인적인 경험을 바탕으로 :

Predicate<String> blank = String::isEmpty;
content.stream()
       .filter(blank.negate())


답변

또 다른 옵션은 모호하지 않은 컨텍스트에서 람다 캐스팅을 한 클래스로 활용하는 것입니다.

public static class Lambdas {
    public static <T> Predicate<T> as(Predicate<T> predicate){
        return predicate;
    }

    public static <T> Consumer<T> as(Consumer<T> consumer){
        return consumer;
    }

    public static <T> Supplier<T> as(Supplier<T> supplier){
        return supplier;
    }

    public static <T, R> Function<T, R> as(Function<T, R> function){
        return function;
    }

}

… 그리고 유틸리티 클래스를 정적으로 가져옵니다 :

stream.filter(as(String::isEmpty).negate())


답변

Predicate#negate당신이 찾고있는 것이 아니어야합니까 ?