问题描述
首先:我非常喜欢龙目岛项目。很棒的工具!这个“编译时”库有很多优秀的方面。
喜欢 @ExtensionMethod
,我已经多次点击这个“功能”,所以现在是我提出这个问题的时候了:
假设我有以下类:
@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);
}
}
输出:
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
- 这是为什么?
- 为什么在第一个示例中没有调用
message(String)
,即使它比message(Object)
具有更好的方法签名拟合? - 为什么
@ExtensionMethod
依赖于参数的顺序?
这是我盲目的假设:
- 解析ExtensionMethods时,Lombok会从左到右处理注解值
-
- 对于
Run_Object_String
,这意味着:首先是AObject
,然后是AString
- 对于
-
- 对于
Run_String_Object
,这意味着:首先是AString
,然后是AObject
- 对于
- Object-String:当将
AObject
修补到类Run_Object_String
中时,将添加message(Object)
方法。并且在使用AString
方法修补message(String)
时,不会添加它。 大概是因为message(Object)
也匹配对message(String)
的调用,所以不会添加message(String)
。 - String-Object:当将
AString
修补到类Run_String_Object
中时,将添加message(String)
方法。 当在AObject
类中使用message(Object)
打补丁时,旧的和现在的message(String)
方法将不接受调用message(Object)
,因此将添加方法message(Object)
。
那么,除了非常注意添加 @UtilityClass
引用的顺序之外,还有其他解决方案吗?
解决方法
这是我不知道的 Lombok 的一个迷人用法。我认为您可以深入寻找答案的最佳地点是来源本身,因为有关这项实验工作的文档似乎很简单,可以理解。
在此处查看 git:HandleExtensionMethod。
根据逻辑,我猜测从注释中有效“拟合”正确方法的区域如下..
与其尝试“最佳”匹配,不如尝试“第一次”匹配。
也就是说,它似乎迭代 List<Extension> extensions
。由于它是一个 Java 列表,我们假设按照扩展在原始注释中指定的顺序进行排序。
它似乎只是按照列表和 return
的顺序工作,只要某些内容与正确的方法和类型形状相匹配。
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;
}
}
您可以查看其余代码以获取其他见解,因为那里似乎没有太多内容(即行数)可供查看,尽管不可否认,在该领域中这是一项令人印象深刻的壮举。