Re: Method references to same instance+method yield different method reference instances

Timo Kinnunen timo.kinnunen at gmail.com
Mon Jan 20 10:34:52 PST 2014


I was kinda bummed when I found out method references aren’t like a pointer to a ‘thunk’ function which adjusts the receiver pointer so that like magic the correct virtual function gets called with just two assembler instructions worth of overhead. How cool would it be to call instance methods with nothing but a pointer existing of the receiver?


Alas, it’s not quite that good but here’s something that at least works (with javac, but not ECJ):


public void setUp() {

System.out.println("setUp begin " + this);

registry.addStaticEventListener(makeCallBack_static());

System.out.println("setUp done " + this);

}

public void tearDown() {

System.out.println("tearDown begin " + this);

registry.removeStaticEventListener(makeCallBack_static());

System.out.println("tearDown done " + this);

}

private static LambdaEventListener makeCallBack_static() {

return WhatAboutCallback::react_static;

}

private static void react_static(Object this2, Notification notification) {

WhatAboutCallback self = (WhatAboutCallback) this2;

self.react(notification);

}

private void react(Notification notification) {

System.out.printf("Notified of %s for %s.\n", notification, this);

}





The trick is the combination of only one call-site (why the JVM links two identical invokedynamic instructions separately I don’t know) and targeting a static method. The static method can then be called with the intended receiver taking up its first parameter, a slot normally used for an instance method’s receiver..


Be as it may, the result I get is:


setUp begin WhatAboutCallback [registry=Registry [eventListeners=[], staticEventListeners=[], noargsEventListeners=[], noargsStaticEventListeners=[], sequence=0, lastNotification=null]]

setUp done WhatAboutCallback [registry=Registry [eventListeners=[], staticEventListeners=[oh.lambda.WhatAboutCallback$$Lambda$3/274064559 at 1c6b6478], noargsEventListeners=[], noargsStaticEventListeners=[], sequence=0, lastNotification=null]]

. . .

tearDown begin WhatAboutCallback [registry=Registry [eventListeners=[], staticEventListeners=[oh.lambda.WhatAboutCallback$$Lambda$3/274064559 at 1c6b6478], noargsEventListeners=[], noargsStaticEventListeners=[], sequence=6, lastNotification=null]]

tearDown done WhatAboutCallback [registry=Registry [eventListeners=[], staticEventListeners=[], noargsEventListeners=[], noargsStaticEventListeners=[], sequence=6, lastNotification=null]]





-- 
Have a nice day,
Timo.

Sent from Windows Mail





From: Remi Forax
Sent: ‎Monday‎, ‎January‎ ‎20‎, ‎2014 ‎12‎:‎00
To: lambda-dev at openjdk.java.net





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