54

I see that the class of a lambda is isSynthetic() && !isLocalOrAnonymousClass(), but I presume that the same may be true for proxy classes.

Of course, I could check that getDeclaredMethods().length == 1 and apply regexp to the class name.

However I want to know if there is a more elegant and robust option to find out if a given object is a lambda.

5
  • 1
    Just wondering, why don't you want to match proxy classes? Commented May 26, 2014 at 13:01
  • 6
    Why do you want to know? Just interested in what you're use case is. Commented May 26, 2014 at 13:03
  • Note that getDeclaredMethods().length == 1 only holds if the Lambda is not Serializable. Then, it will generate a .writeReplace() as well. Commented Jul 30, 2014 at 17:28
  • 1
    Right, I know that. In my case all Lambdas are my own. So, I have a control over them: github.com/spring-projects/spring-integration-extensions/tree/… Commented Jul 30, 2014 at 18:07
  • I found this question because I was making an interface with 2 method overloads: one taking in a <T extends AutoCloseable> and another Supplier<T extends AutoCloseable>. Funny enough, lambdas implement AutoCloseable and it would not compile because it could not disambiguate which overload to call if I passed in a Supplier lambda. Commented Jan 26, 2017 at 18:23

4 Answers 4

63

There is no official way to do this, by design. Lambdas are part of the language; and are integrated into the type system through functional interfaces. There should be no need to distinguish a Runnable that began life as a lambda, a named class, or an inner class -- they're all Runnables. If you think you have to "deal with lambda" by taking apart the class file, you're almost certainly doing something wrong!

Sign up to request clarification or add additional context in comments.

25 Comments

Those questions suggest that you are not fully on board with how erasure works in implementing Java generics. This has nothing to do with lambda; the same issues show up with named classes.
I'm not sure that I agree with you, because we can do so mach with reflection and generics at runtime, that it looks like Java hasn't done enough for lambdas. We often use generic resolution and reflection info at runtime to make many thing flexible and usefull for end-users, e.g. jira.spring.io/browse/SPR-10675. We resolve it like GenericTypeResolver.resolveTypeArgument(listenerType, ApplicationListener.class), but since lambda doesn't provide generic info we end up with issue.
To your "I'm not sure I agree with you" response: this is pure wishful thinking. Yes, there are a few corner cases in which you can get reflection to cough up some compile-time type decisions (specifically with anonymous classes), but these are the exceptions, not the other way around. Generics are erased; reflection is for reflecting over CLASSES, not INSTANCES. I can imagine you'd want it to work differently, of course, but you're looking for "how do I" answers, and they're not there.
@Holger: That technique is pretty dangerous. Serialized lambdas have significantly higher performance costs compared to nonserializable lambdas, and are less secure (serialization is inherently a public hidden constructor to behavior and/or captured data that the programmer might have intended to stay private.) This is not a free lunch.
@damluar Too many reasons to fit in 600 characters! This is a classic example of one of those ideas that is obvious and yet in hindsight turned out to be the wrong thing to do. See youtube.com/watch?v=MLksirK9nnE for some of the reasons.
|
2

A simple way to check if a Runnable - or any other functional interface - has been implemented by a Lambda is to check if the class is synthetic but the interface's function isn't:

Class clz = suspectedObject.getClass();
var isLambda = clz.isSynthetic() && !clz.getDeclaredMethod("run").isSynthetic();

Note that this will also detect function references (this::run) - I don't think there's a way to distinguish between that form and an anonymous lambda.

I want to use this method to log the type of the object in a nicer way - my framework sometime logs the names of provided interface implementations to let the user know in which part of their code they have a problem, and lambda's toString() is next to useless. Not that I've yet figured out what is a useful way to represent the notion of "you should look at this specific lambda and not any of the other dozens of lambdas in your class".

Comments

0

If you know the lambda extends Serializable you could check the synthetic writeReplace method that is generated returns a SerializedLambda as shown below.

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.function.Function;

public class IsLambdaExample
{
    public static void main( String[] args )
    {
        System.out.println(
            "Is anonymous inner class a lambda: "+isLambda(
                new SerialisableFunction<Object,Object>(){ public Object apply( Object o ){ return null; } }
            )
        );
        System.out.println( "Is lambda a lambda: "+isLambda( (SerialisableFunction<Object,Object>)o -> null ) );
        System.out.println(
            "Is proxy instance a lambda: "+isLambda(
                (SerialisableFunction)Proxy.newProxyInstance(
                    ClassLoader.getSystemClassLoader(),
                    new Class[]{ SerialisableFunction.class },
                    new InvocationHandler()
                    {
                        @Override
                        public Object invoke( Object proxy, Method method, Object[] args )
                        {
                            return null;
                        }

                        private SerializedLambda writeReplace()
                        {
                            return new SerializedLambda( InvocationHandler.class, "", "", "", 0, "", "", "", "", new Object[]{} );
                        }
                    }
                )
            )
        );
    }

    public static <T extends Function<?,?> & Serializable> Boolean isLambda( T potentialLambda )
    {
        try{
            Class<?> potentialLambdaClass = potentialLambda.getClass();
            if( !potentialLambdaClass.isSynthetic() ){
                return false;
            }
            Method writeReplace = potentialLambdaClass.getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            Object writeReplaceObject = writeReplace.invoke(potentialLambda);
            return writeReplaceObject != null && SerializedLambda.class.isAssignableFrom( writeReplaceObject.getClass() );
        }
        catch( NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored ){
            return false;
        }
    }

    interface SerialisableFunction<T,U> extends Function<T,U>, Serializable {}
}

Comments

-2
public static boolean isLambda(Object obj) {
    return obj.getClass().toString().contains("$$Lambda$");
}

4 Comments

No, this does not appear valid.
It won't work because nothing can stop me from naming my class "My$$Lambda$1" for example :-)
I would replace toString() with getSimpleName(). It shortens the search for $$Lambda$.
In java 21+, it should be replaced with .contains("$$Lambda");, there is no ending $

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.