Method references to same instance+method yield different method reference instances
Remi Forax
forax at univ-mlv.fr
Mon Jan 20 02:00:36 PST 2014
On 01/20/2014 10:10 AM, Nick Williams wrote:
> I'm a little confused by this behavior, so I wanted to verify that this is according to spec and not a bug. Consider the following code:
>
> public class SomeClass
> {
> @AfterConstruct
> public void setUp() {
> ...
> registry.addEventListener(this::react);
> ...
> }
>
> @PreDestroy
> public void tearDown() {
> ...
> registry.removeEventListener(this::react);
> ...
> }
>
> private void react(SomeEvent event) {
> // does something with event
> }
> }
>
> I expected this to work, but it does not. The event registry contains a Hashtable of event listeners. The two method references to this::react are identical, so in my mind it would seem that they should pass in the same instance, but they do not. Upon debugging, the method references have different hash codes. This is not only counter-intuitive, but it seems like it's counter-performance, as well. IMO, the method reference this::react should be cached, and all uses of it should use the same instance.
It's perhaps counter-intuitive but it's clearly not counter-performance.
The spec is very clear that lambda or method reference (internally there
are the same kind of object) have no identity.
As you said, otherwise there is a cache somewhere, a very very expensive
cache because 'this::react' means that you have 'specialized'
(bound/curry) a reference to a method depending on an instance so you
need a cache that is able to store for *any instances* of a class a
corresponding method reference. This kind of weak concurrent hash cache
that can store potentially every objects are incredibly expensive.
>
> What does the spec say about this?
don't do that :)
You can store the listener in setUp and use it in tearDown, and ..
... then rewrite a new version of JUnit that use a high order function
(a function that take a unit test)
instead of using the couple setUp/tearDown.
doSetUpAndTearDown(() -> {
// my unit test
});
in that case you can use a local variable to store you event listener.
>
> Nick
>
Rémi
More information about the lambda-dev
mailing list