28

If I obtain a method reference using the new syntax:

anObject::aMethod

Do I always get the same object? That is, can I trust that two references to the same method will be equal?

This is good to know if, for example, I plan to use them as Runnable callbacks that I can add and remove:

someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)

Would this require saving the reference in a Runnable variable to keep it stable?

5
  • how is removeCallback() implemented ? Commented Jun 7, 2018 at 19:07
  • 4
    Lambdas don't have equals and hashCode - see LambdaMetafactory. Commented Jun 7, 2018 at 19:32
  • 3
    Is there a way to compare lambdas? Commented Jun 7, 2018 at 19:47
  • 3
    Two exact method references are not equal Commented Jun 7, 2018 at 19:48
  • Incidentally, I do not think this is a duplicate. Method references use a syntax that doesn't allow capturing variables in a closure, except this. It's not clear that this follows the same rules as lambda expressions (which have every reason NOT to be stable references) Commented Jun 8, 2018 at 14:12

2 Answers 2

21

JLS makes no promises about identity or equality of what you get out of method reference expressions.

You can run a quick test:

Object obj = new Object();

IntSupplier foo = obj::hashCode;
IntSupplier bar = obj::hashCode;

System.out.println(foo == bar);  // false

System.out.println(foo.equals(bar));  // false      

But this is, of course, implementation dependent.

You could make your lambda Serializable and key your callback map with the serlialized representation. See How to serialize a lambda?. While this will work, it's not exactly required to work by the specs.

Sign up to request clarification or add additional context in comments.

Comments

7

Just try this out to get the answer:

Object object = ...;
Supplier<String> s1 = object::toString;
Supplier<String> s2 = object::toString;
System.out.println(s1.equals(s2));

And the answer is... unfortunately not.

OF course if you keep the same reference (i.e. the same object), it will work; but if, as the example above, you request two lambdas, although they seem to be identical, they will never be equal. Therefore reference = object::methodand then later remove(reference) will obviously work, but remove(sameObject::sameMethod) from a collection will never work if it is written literaly as such.

The answer is also no for constructor (e.g. ArrayList::new) and unbound methods (e.g. Object::toString). It seems that a new lambda is constructed each time you use a lambda expression.

As @Hitobat points it out, this unequality makes sense if you think about what exactly are lambdas and where do they come from. Basicly, Supplier<String> x = myObject::toString is a syntactic suggar for Supplier<String> x = new Supplier<String>( ... ). Without a proper Object.equals overloading, two instances of an anonymous class are obviously different. As many people probably, I though that there was a kind of cache of frequently used lambdas somewhere to make it more efficient; well, not at all.

3 Comments

I guess this makes sense in retrospect. If you think of the method reference as just syntactic sugar for the longer anonymous class "new Supplier() { ... }" then without overloading Object.equals these anon instances won't match.
Exact. I will add a precision about this to my answer.
As explained in Does a lambda expression create an object on the heap every time it's executed?, the specification allows for object reusual and it does already happen under certain circumstances (i.e. non-capturing lambdas), but is left entirely at the discretion of the JVM. Since JVM implementations also have Escape Analysis which may elide object creation transparently (i.e. supplier1 == supplier2 gets replaced by the constant false but the objects may not even exist), that’s reasonable. The performance matters, not the result of x == y

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.