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?
That's all about what is method reference is in Java 8 and how you can use it to write clean code in Java 8. The biggest benefit of the method reference or constructor reference is that they make the code even shorter by eliminating lambda expression, which makes the code more readable.
The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.
If you have been coding in Java 8 then you may know that using method reference in place of lambda expression makes your code more readable, hence it is advised to replace lambda expression with method reference wherever possible.
Method references are a special type of lambda expressions. They're often used to create simple lambda expressions by referencing existing methods. There are four kinds of method references: Static methods. Instance methods of particular objects.
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.
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::method
and 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With