Hervian / lambda-factory

A fast alternative to Java Reflection API's method invocation
Apache License 2.0
119 stars 33 forks source link

Does not work with non-static methods #11

Closed lukehutch closed 4 years ago

lukehutch commented 5 years ago

Given the following interface and implementing class:

    @FunctionalInterface
    public interface AddOne<Y> {
        void addOne(Y y);
    }

    public class Z implements AddOne<Integer> {
        @Override
        public void addOne(Integer y) {
            System.out.println(y + 1);
        }
    }

The following code gives java.lang.AbstractMethodError: Receiver class [...]$Z$$Lambda$82/0x0000000800200040 does not define or inherit an implementation of the resolved method abstract invoke_for_void(Ljava/lang/Object;)V of interface com.hervian.lambda.Lambda.

    try {
        Method addOne = Z.class.getMethod("addOne", Integer.class);
        Lambda z = LambdaFactory.create(addOne);
        z.invoke_for_void(Integer.valueOf(1));
    } catch (Throwable e) {
        e.printStackTrace();
    }

However changing class Z to the following works:

    public class Z {
        public static void addOne(Integer y) {
            System.out.println(y + 1);
        }
    }

Lambdas in Java are typically non-static, since they are created from SAM interfaces, so it would be nice to have this working in the original form.

You would probably have to somehow replicate Java's logic used to determine if a lambda or inner class captures any variables, in order to determine whether you need to allocate a new instance of the defining class every time the method is invoked (maybe disallow this case), or whether you can allocate just one instance, and reuse it for each invocation.

lukehutch commented 5 years ago

I got an answer on StackOverflow about the right way to do this:

https://stackoverflow.com/questions/52802611/lambdametafactory-with-concrete-implementation-of-generic-type/52802782#52802782

lukehutch commented 5 years ago

In fact given the response, it might be useful to create a lambda factory method that you just pass a SAM object instance into to turn it into a lambda.

Hervian commented 5 years ago

Sorry for the late reply and thanks for the link and analysis. I will look into this when I get the time. Feel free to issue a Pull Request in case you have already solved it. /Anders

Hervian commented 4 years ago

I @lukehutch

Are sure this does not work, if you pass the instance as the first parameter to the invoke method, just like with java.lang.Method's invoke? I will check myself monday.

lukehutch commented 4 years ago

Sorry, it has been too long, and I don't remember any of the complex details anymore. Hopefully the links I provided will help.

Hervian commented 4 years ago

I have tested your code, and the problem is that you are creating a lambda of an instance method, but are invoking without any instance. You must invoke like this:

z.invoke_for_void(new Z(), Integer.valueOf(1));

lukehutch commented 4 years ago

Ah, OK, thanks and sorry for the trouble.