javaaoplombokaspectmethod-signature

Lombok extension methods: Prevalence/priority?


First off: I absolutely LOVE Project Lombok. Awesome tool! There's so many excellent aspects to this 'compile time' library.

Loving the @ExtensionMethods, I have already hit this 'feature' a few times, so now it's time for me to ask this question:

Suppose I have the following classes:

@UtilityClass
public class AObject {
    static public String message(final Object pObject) {
        return "AObject = " + (pObject != null);
    }
}
@UtilityClass
public class AString {
    static public String message(final String pObject) {
        return "AString = " + (pObject != null);
    }
}
@ExtensionMethod({ AObject.class, AString.class })
public class Run_Object_String {
    public static void main(final String[] args) {
        System.out.println("\nRun_Object_String.main()");
        final String s = "Bier!";
        final Object o = new Object();

        System.out.println("Testing s: " + s.message());
        System.out.println("Testing o: " + o.message());
        System.out.println("Testing s: " + s.message());
    }
}
@ExtensionMethod({ AString.class, AObject.class })
public class Run_String_Object {
    public static void main(final String[] args) {
        System.out.println("\nRun_String_Object.main()");
        final String s = "Bier!";
        final Object o = new Object();

        System.out.println("Testing s: " + s.message());
        System.out.println("Testing o: " + o.message());
        System.out.println("Testing s: " + s.message());
    }
}
public class ClassPrevalenceTest {
    public static void main(final String[] args) {
        Run_Object_String.main(args);
        Run_String_Object.main(args);
    }
}

With the output:

Run_Object_String.main()
Testing s: AObject = true
Testing o: AObject = true
Testing s: AObject = true

Run_String_Object.main()
Testing s: AString = true
Testing o: AObject = true
Testing s: AString = true

Here's what I blindly assume:

So, apart from taking great care of what order I add the @UtilityClass references, are there any other solutions to this?


Solution

  • This is a fascinating use of Lombok I wasn't aware of. The best place I think you could delve to find your answers is the source itself since the docs on this experimental work seems pretty light, understandably.

    Take a look on git here: HandleExtensionMethod.

    I am guessing based on the logic that the area that's effectively "fitting" the right method from the annotation is as below..

    Instead of trying for a "best" fit, it seems to be aiming for a "first" fit.

    That is, it appears to iterate over List<Extension> extensions. Since it's a Java list, we assume ordering is preserved in the order the extensions were specified in the original annotation.

    It appears to simply work in order of the list and return as soon as something matches the right method and type shape.

    Types types = Types.instance(annotationNode.getContext());
            for (Extension extension : extensions) {
                TypeSymbol extensionProvider = extension.extensionProvider;
                if (surroundingTypeSymbol == extensionProvider) continue;
                for (MethodSymbol extensionMethod : extension.extensionMethods) {
                    if (!methodName.equals(extensionMethod.name.toString())) continue;
                    Type extensionMethodType = extensionMethod.type;
                    if (!MethodType.class.isInstance(extensionMethodType) && !ForAll.class.isInstance(extensionMethodType)) continue;
                    Type firstArgType = types.erasure(extensionMethodType.asMethodType().argtypes.get(0));
                    if (!types.isAssignable(receiverType, firstArgType)) continue;
                    methodCall.args = methodCall.args.prepend(receiver);
                    methodCall.meth = chainDotsString(annotationNode, extensionProvider.toString() + "." + methodName);
                    recursiveSetGeneratedBy(methodCall.meth, methodCallNode);
                    return;
                }
            }
    

    You can look at the rest of the code for other insight as there doesn't seem to be too much there (i.e. number of lines) to look at, though admittedly it's an impressive enough a feat to do in that space.