从 Groovy 2.1.0 开始,您可以使用@groovy.transform.CompileDynamic 注解来声明必须动态调用的代码部分。在这种情况下,您可以将show(a) 方法调用提取到使用@CompileDynamic 注释的单独方法中。代码的其余部分将静态编译,show(a) 方法调用的这个小例外。
考虑以下示例:
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
@CompileStatic
class PatternMatching {
static void main(String[] args) {
[new A(), new B(), new C()].each {
callShowDynamically(it)
}
}
@CompileDynamic
static void callShowDynamically(A a) {
show a
}
static void show(A a) {
print 'A'
}
static void show(B b) {
print 'B'
}
static void show(C c) {
print 'C'
}
}
@CompileStatic
class A {}
@CompileStatic
class B extends A {}
@CompileStatic
class C extends A {}
这里是反编译的PatternMatching.class 的并排比较。 (右边的代表一个静态编译的类,带有@CompileDynamic方法。)
这里是each闭包反编译类字节码的并排比较:
注意一个细节——即使我们使用带有@CompileDynamic 注释的callShowDynamically() 方法,它在字节码级别的调用使用静态编译类型,甚至将其转换为A 类型:
public static void main(String... args) {
final class _main_closure1 extends Closure implements GeneratedClosure {
public _main_closure1(Object _outerInstance, Object _thisObject) {
super(_outerInstance, _thisObject);
}
public Object doCall(Object it) {
return PatternMatching.callShowDynamically((A)ScriptBytecodeAdapter.castToType(it, A.class));
}
public Object call(Object args) {
return this.doCall(args);
}
public Object call() {
return this.doCall((Object)null);
}
@Generated
public Object doCall() {
return this.doCall((Object)null);
}
}
DefaultGroovyMethods.each(ScriptBytecodeAdapter.createList(new Object[]{new A(), new B(), new C()}), new _main_closure1(PatternMatching.class, PatternMatching.class));
}
使这个调用“动态”的是PatternMatching.callShowDynamically()方法的实现:
public static Object callShowDynamically(A a) {
CallSite[] var1 = $getCallSiteArray();
return var1[0].callStatic(PatternMatching.class, a);
}
此方法使用Groovy's MOP (Meta-Object Protocol) 来检测(基于运行时特定类型)该类型要调用的最特定方法是什么。在编译器级别,它仍然只是PatternMatching.callShowDynamically((A)value) 方法调用,因此它不知道可能会调用其他方法。这就是为什么您的 IntelliJ IDEA 将 show(B b) 和 show(C c) 方法视为未使用的原因,因为没有显式调用这两个方法中的任何一个。