5

I reference the same method twice but the references are different. See this example:

import java.util.function.Consumer;

public class MethodRefTest {
    public static void main(String[] args) {
        new MethodRefTest();
    }

    public MethodRefTest() {
        Consumer<Integer> a = this::method;
        System.out.println(a);
        Consumer<Integer> b = this::method;
        System.out.println(b);
    }

    public void method(Integer value) {

    }
}

The output is:

MethodRefTest$$Lambda$1/250421012@4c873330
MethodRefTest$$Lambda$2/295530567@776ec8df

Are method references nothing more than syntactic sugar for anonymous classes? If not, what do I have to do to always get the same method reference? (Aside from storing a reference once in a field to work with.)

(Application: I thought of method references as a prettier way of observer implementation. But with different references each time it's impossible to remove an observer from an observable once it's added.)

2240
  • 1,547
  • 2
  • 12
  • 30
tobywoby
  • 85
  • 5
  • I'm away from a terminal right now, but I'd guess that the hashCode for a Consumer object is a pointer to the actual method location. In other words, you have two separate Consumer objects, which have different memory locations, but they both point to the same method(). Take a look at the Consumer source for more info. Edit: I meant the runtime Consumer implementation in a debugger. – Lukas Bradley Oct 06 '17 at 19:35
  • Don't overthink it. The hash code is whatever `Object.hashCode()` returns. It won't have anything to do with the method the `Consumer` is calling. It's not like a `Consumer` will have some reference to the method; all it has is an implementation of `accept()` that does something or other. – John Kugelman Oct 06 '17 at 19:43

4 Answers4

3

Are method references nothing more than syntactic sugar for anonymous classes?

Correct. They aren't necessarily always implemented as heavyweight as that, but conceptually it's all they are.

If not, what do I have to do to always get the same method reference? (Aside from storing a reference once in a field to work with.)

Store the reference in a field. That's the answer. (Sorry.)

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
1

You ask,

Are method references nothing more than syntactic sugar for anonymous classes?

The JLS says that

Evaluation of a method reference expression produces an instance of a functional interface type

(JLS 8, section 15.13)

That doesn't explicitly require an anonymous class, but it does require some class, and it does not provide a mechanism for naming that class. I can imagine alternatives, but using the existing anonymous class mechanism seems pretty natural.

It is plausible that an implementation could recognize multiple references to the same method and use the same anonymous class for them, but such behavior is by no means required, and you have demonstrated that your implementation does not do it. Even if an implementation did do that, however, the JLS is at minimum suggestive that each evaluation of a method reference expression produces a new object.

You go on,

If not, what do I have to do to always get the same method reference? (Aside from storing a reference once in a field to work with.)

Your only guaranteed mechanism is to evaluate the method reference just once, and to then hold on to a reference to the resulting object as long as you have need of it. Storing the reference in a field, as @JohnKugelman describes, is one variation on that, but depending on the scope in which you need to refer to the same method reference object, it might suffice to store it in a local variable or to pass it around via method arguments.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

Generally speaking the easiest and efficient way is storing the reference either in a field or a (local) variable.

Fman110
  • 1
  • 2
0

First of all, the toString() output of the object generated for a method reference is entirely unspecified, hence, you cannot draw any conclusions about the object’s identity from there. Also, the hexadecimal number is an implementation dependent hashcode that is rarely an address. The only reliable way to check the identity of the object is to do a==b.

Still, these objects are indeed different, but that’s an implementation detail. As explained in Does a lambda expression create an object on the heap every time it's executed?, the JVM has a lot of freedom regarding object reuse, but the current HotSpot/OpenJDK implementation will reuse objects only for non-capturing expressions and this::method is capturing the this reference. Also, as shown by the code of your question, each occurrence of this::method in your code will even get its own generated class.

This is explained in Is method reference caching a good idea in Java 8?, which also concludes that you shouldn’t keep these instances for performance reasons. But in your case, when you want to deregister a listener reliably, keeping the reference in a variable is the only way to do it. As even for a single occurrence of a non-capturing expression, for which the current implementation will provide the same object, there is no guaranty that this will work, as this reuse still is an implemen­tation dependent behavior.

Holger
  • 285,553
  • 42
  • 434
  • 765