3

Is there a way to determine whether a given Java lambda object is a method reference or a "CallSite-specific" lambda:

boolean isMethodReference(Object lambda)

Positive example:

assertTrue(isMethodReference(Object::toString));

Negative example with "CallSite-specific" lambda:

long valueFromCallSite = System.currentTimeMillis();
Consumer<Object> lambda = o -> {
    if (valueFromCallSite % 2 == 0) {
        o.toString();
    } else {
        o.hashCode();
    }
};
assertFalse(isMethodReference(lambda));
Benedikt Waldvogel
  • 12,406
  • 8
  • 49
  • 61
  • 2
    Running `.getClass()` on the instance will return a dynamic/synthetic class. You shouldn't rely on this. Your program shouldn't care where the instance came from. – Savior Apr 25 '16 at 16:21
  • 3
    What's your use case? (Just curious, can't think myself of a reason to know/care.) – davidbak Apr 25 '16 at 16:24
  • My answer to this question is "I sure hope there _isn't_ a way." – Louis Wasserman Apr 25 '16 at 16:35
  • Allow me to second the use-case request ... it may be that knowing *why* you want this may allow someone to provide a better way to your ultimate goal. – dcsohl Apr 25 '16 at 17:47
  • Beyond the source code, a lambda expression is just a method reference to a synthetic method. But even source code method references may point to a synthetic helper method under the hood in certain cases. So there is no safe way to differentiate them on the technical level. Semantically, the desire to differentiate them makes no sense anyway. – Holger Apr 26 '16 at 09:45
  • It strikes me that the choices extend beyond method reference and lambda expression. What about passing an instance of `class Foo implements Consumer`? – Hank D Apr 28 '16 at 05:03
  • @Hank D: another option is to generate an implementation via `java.lang.reflect.Proxy`. – Holger Apr 28 '16 at 08:44

2 Answers2

2

A heuristic approach for isMethodReference(lambda) was proposed in "Determine if a lambda expression is stateless or stateful in Java":

boolean isMethodReference(Object lambda) {
    return lambda.getClass().getDeclaredFields().length == 0;
}

It’s only a heuristic because it relies on unspecified behavior and thus is JDK implementation-specific and even might break in a future version.

Benedikt Waldvogel
  • 12,406
  • 8
  • 49
  • 61
  • Indeed very heuristic. Seems like method references create private final instance variables within an anonymous class to hold the parameters before executing. – downvoteit Oct 28 '21 at 23:12
0

It is possible to determine if a Java method reference is equivalent to another method reference. Assuming we have an interface User:

public interface User {
    String firstName();
}

then we can do this:

public class Main {

public static void main(String[] args) {
    print(User::firstName);
    print(u -> u.firstName());
}

public interface SerializableFunction<T, R> extends Serializable, Function<T, R> {
}

private static void print(SerializableFunction<User, String> function) {
    System.out.println("function = " + function);
    if (Arrays.equals(serialize(function), serialize(User::firstName))) {
        System.out.println("which is the method reference User::firstName");
    }
}

private static byte[] serialize(SerializableFunction<User, String> function) {
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
        objectOutputStream.writeObject(function);
        return byteArrayOutputStream.toByteArray();
    } catch (IOException e) {
        return new byte[0];
    }
}

}

This will print something like this:

function = software.chronicle.refactor.demo.serialization.Main$$Lambda$25/0x0000000800c02658@65ab7765

which is the method reference User::firstName

function = software.chronicle.refactor.demo.serialization.Main$$Lambda$33/0x0000000800c02b08@6659c656

So, it is actually possible to check if a lambda is a specific method reference.