3

I am new to java8 and I was trying to understand this piece of code. This is the piece of code:

Stream.of("A", "B", "C").anyMatch(someObj.getStringValue()::equalsIgnoreCase)

someObj.getStringValue() references some object and the getStringValue() returns some String value.

What is the equivalent predicate of the method reference being passed into anyMatch(...)?

My understanding was this would be equivalent to:

Predicate<String> p = new Predicate<String>() {
    @Override
    public boolean test(String t) {
        return someObject.getStringValue().equalsIgnoreCase(t);
    }
}
Stream.of("A", "B", "C").anyMatch(p)

With this I get the error "Local variable someObject defined in an enclosing scope must be final or effectively final." Any explanation on this is appreciated.

user3303411
  • 57
  • 1
  • 7
  • 1
    See [this answer](https://stackoverflow.com/a/20938132/2928853) for a good explanation of what final and effectively final mean – jrook Oct 10 '19 at 23:55
  • 2
    it's a well-asked question, but I am afraid it's a duplicate – Andrew Tobilko Oct 10 '19 at 23:56
  • 1
    To understand a code piece, you don't necessarily need to rewrite it to a simpler construct. `someObj.getStringValue()::equalsIgnoreCase` is concise and expressive. I would check `someObj` on `null` and save `someObj.getStringValue()` to a varible, and use that varible like `thatVarible::equalsIgnoreCase ` – Andrew Tobilko Oct 10 '19 at 23:57
  • 1
    Even when you were to explicitly declare the `Predicate`, you could remain with using lambda as `Predicate p = t -> someObject.getStringValue().equalsIgnoreCase(t)`, which should take care of the effectively final implementation itself. – Naman Oct 11 '19 at 02:55

1 Answers1

3

The someObj.getStringValue() expression is evaluated outside, so equivalent code would be:

final String x = someObject.getStringValue();
Predicate<String> p = new Predicate<String>() {
    @Override
    public boolean test(String t) {
        return x.equalsIgnoreCase(t);
    }
}
Stream.of("A", "B", "C").anyMatch(p)

where the local variable x is "anonymous" too.

Because of this, someObject does not need to be effectively final.

You can verify this behavior for the lambda expression in a debugger, by putting a breakpoint inside getStringValue(). Even though the test() method is called 3 times (because the stream has 3 elements, and assuming no match), the getStringValue() method will only be called once.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Thanks for that example. Upon further read of https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.13 and http://tutorials.jenkov.com/java/lambda-expressions.html I was able to understand in depth what was happening. – user3303411 Oct 11 '19 at 04:32