Groovy 星级导入和使用 "partial" 包
Groovy star import and usage of "partial" packages
令我惊讶的是,我今天了解到以下内容在 Groovy 中工作得很好:
import java.util.concurrent.*
def atomicBool = new atomic.AtomicBoolean(true)
即star导入后,我可以用一个'partial'包来引用java.util.concurrent.atomic.AtomicBoolean
.
显然,这在 Java 中是无效的:
import java.util.concurrent.*;
public class Sample {
public static void main(String[] args) {
// compiler error: 'atomic' cannot be resolved to a type
new atomic.AtomicBoolean(true);
}
}
所以看来 Groovy 的包的想法在这方面类似于 C++(或 C#)名称空间。
向Groovy专家提问:
- 这是设计使然还是口译员对待明星进口方式的(可能是无意的)副作用?
- 如果是设计使然,您能否指出文档或语言规范中记录此行为的部分? (在 Star Import 的文档中没有提到这一点,据我所知,在语言规范中也没有提到,或者至少我找不到任何东西。)
根据 Groovy 源代码,此行为似乎是有意为之。在我们深入研究 Groovy 内部结构之前,您必须了解一件事 - Groovy 编译为可以由有效 Java 代码表示的字节码。这意味着 Groovy 代码,例如您的示例中的代码实际上编译成这样的代码(没有编译静态和类型检查转换):
import groovy.lang.Binding;
import groovy.lang.Script;
import java.util.concurrent.atomic.AtomicBoolean;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
AtomicBoolean atomicBool = (AtomicBoolean)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(AtomicBoolean.class, true), AtomicBoolean.class);
return var1[2].callCurrent(this, atomicBool);
}
}
如您所见,Java class 使用完整的 java.util.concurrent.atomic.AtomicBoolean
导入,这实际上是 Groovy 将您的输入源代码转换为的内容。
这是怎么发生的?
如您所知,Groovy 从输入源文件构建抽象语法树 (AST),并遍历所有节点(如表达式、变量定义、方法调用等)并应用转换。 Groovy 使用 class 称为 ResolverVisitor
that is designed to resolve types. When Groovy compiles your code it finds ConstructorCallExpression
:
new atomic.AtomicBoolean(true)
它发现您尝试创建的对象的预期类型是 atomic.AtomicBoolean
,因此 ResolverVisitor
通过调用 resolveOrFail(type, cce);
at line 1131 开始解析类型。
它尝试了几种失败的解析策略until it reaches method resolveFromModule
at line 695。这里发生的是它遍历所有星形导入(在你的情况下是单个 java.util.concurrent.*
),然后它将星形导入与类型名称连接起来,并检查由此连接创建的限定名称是否是有效类型 class .幸运的是你的情况:
当类型被解析时,Groovy 在抽象语法树中用这个解析的有效类型名称替换初始类型。完成此操作后,您的输入代码看起来更像这样:
import java.util.concurrent.*
java.util.concurrent.atomic.AtomicBoolean atomicBool = new java.util.concurrent.atomic.AtomicBoolean(true)
这是编译器最终得到的。当然,完全限定名称会被导入替换(这就是 Java 编译器对限定名称所做的)。
这个"feature"是designed介绍的吗?
我不能告诉你。然而,我们可以从源代码中读到,这是故意发生的,并且像这样的类型解析是有意实现的。
为什么没有记录?
我想实际上没有人推荐以这种方式使用导入。 Groovy 非常强大,您可以通过多种不同的方式做很多事情,但这并不意味着您应该这样做。星号导入颇具争议,因为在显式导入上使用星号导入会使您的代码由于可能的 class 导入冲突而更容易出错。但是,如果您想知道此类问题的确切答案,您将不得不询问 Groovy 语言设计者和核心开发人员 - 他们可能会毫无疑问地给您直接的答案。
令我惊讶的是,我今天了解到以下内容在 Groovy 中工作得很好:
import java.util.concurrent.*
def atomicBool = new atomic.AtomicBoolean(true)
即star导入后,我可以用一个'partial'包来引用java.util.concurrent.atomic.AtomicBoolean
.
显然,这在 Java 中是无效的:
import java.util.concurrent.*;
public class Sample {
public static void main(String[] args) {
// compiler error: 'atomic' cannot be resolved to a type
new atomic.AtomicBoolean(true);
}
}
所以看来 Groovy 的包的想法在这方面类似于 C++(或 C#)名称空间。
向Groovy专家提问:
- 这是设计使然还是口译员对待明星进口方式的(可能是无意的)副作用?
- 如果是设计使然,您能否指出文档或语言规范中记录此行为的部分? (在 Star Import 的文档中没有提到这一点,据我所知,在语言规范中也没有提到,或者至少我找不到任何东西。)
根据 Groovy 源代码,此行为似乎是有意为之。在我们深入研究 Groovy 内部结构之前,您必须了解一件事 - Groovy 编译为可以由有效 Java 代码表示的字节码。这意味着 Groovy 代码,例如您的示例中的代码实际上编译成这样的代码(没有编译静态和类型检查转换):
import groovy.lang.Binding;
import groovy.lang.Script;
import java.util.concurrent.atomic.AtomicBoolean;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
AtomicBoolean atomicBool = (AtomicBoolean)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(AtomicBoolean.class, true), AtomicBoolean.class);
return var1[2].callCurrent(this, atomicBool);
}
}
如您所见,Java class 使用完整的 java.util.concurrent.atomic.AtomicBoolean
导入,这实际上是 Groovy 将您的输入源代码转换为的内容。
这是怎么发生的?
如您所知,Groovy 从输入源文件构建抽象语法树 (AST),并遍历所有节点(如表达式、变量定义、方法调用等)并应用转换。 Groovy 使用 class 称为 ResolverVisitor
that is designed to resolve types. When Groovy compiles your code it finds ConstructorCallExpression
:
new atomic.AtomicBoolean(true)
它发现您尝试创建的对象的预期类型是 atomic.AtomicBoolean
,因此 ResolverVisitor
通过调用 resolveOrFail(type, cce);
at line 1131 开始解析类型。
它尝试了几种失败的解析策略until it reaches method resolveFromModule
at line 695。这里发生的是它遍历所有星形导入(在你的情况下是单个 java.util.concurrent.*
),然后它将星形导入与类型名称连接起来,并检查由此连接创建的限定名称是否是有效类型 class .幸运的是你的情况:
当类型被解析时,Groovy 在抽象语法树中用这个解析的有效类型名称替换初始类型。完成此操作后,您的输入代码看起来更像这样:
import java.util.concurrent.*
java.util.concurrent.atomic.AtomicBoolean atomicBool = new java.util.concurrent.atomic.AtomicBoolean(true)
这是编译器最终得到的。当然,完全限定名称会被导入替换(这就是 Java 编译器对限定名称所做的)。
这个"feature"是designed介绍的吗?
我不能告诉你。然而,我们可以从源代码中读到,这是故意发生的,并且像这样的类型解析是有意实现的。
为什么没有记录?
我想实际上没有人推荐以这种方式使用导入。 Groovy 非常强大,您可以通过多种不同的方式做很多事情,但这并不意味着您应该这样做。星号导入颇具争议,因为在显式导入上使用星号导入会使您的代码由于可能的 class 导入冲突而更容易出错。但是,如果您想知道此类问题的确切答案,您将不得不询问 Groovy 语言设计者和核心开发人员 - 他们可能会毫无疑问地给您直接的答案。