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专家提问:

根据 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 语言设计者和核心开发人员 - 他们可能会毫无疑问地给您直接的答案。