簡體   English   中英

Groovy明星導入和使用“部分”包

[英]Groovy star import and usage of “partial” packages

令我驚訝的是,我今天已經了解到以下在Groovy中運行得很好:

import java.util.concurrent.*

def atomicBool = new atomic.AtomicBoolean(true)

即在星型導入之后,我可以使用'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類使用完整的java.util.concurrent.atomic.AtomicBoolean import,這實際上是Groovy將您的輸入源代碼轉換為的內容。

怎么會發生?

您可能知道Groovy從輸入源文件構建抽象語法樹(AST),它迭代所有節點(如表達式,變量定義,方法調用等)並應用轉換。 Groovy使用名為ResolverVisitor類來解析類型。 當Groovy編譯代碼時,它會找到ConstructorCallExpression

new atomic.AtomicBoolean(true)

它看到您嘗試創建的對象的預期類型是atomic.AtomicBoolean ,因此ResolverVisitor通過調用resolveOrFail(type, cce);開始解析該類型resolveOrFail(type, cce); 在1131線

它嘗試了幾種失敗的解決策略, 直到它到達第695行的方法resolveFromModule 這里發生的是迭代所有星型導入(在您的情況下單個java.util.concurrent.* ),然后它將星型導入與類型名稱連接,並檢查從此串聯創建的限定名稱是否是有效的類型類。 幸運的是,在你的情況下:

在此輸入圖像描述

當類型得到解析時,Groovy會在抽象語法樹中使用此已解析的有效類型名稱替換初始類型。 在此操作之后,您的輸入代碼看起來更像是:

import java.util.concurrent.*

java.util.concurrent.atomic.AtomicBoolean atomicBool = new java.util.concurrent.atomic.AtomicBoolean(true)

這是編譯器最終得到的。 當然,完全限定名稱會被導入替換(這是Java編譯器使用限定名稱執行的操作)。

這個“功能”是由設計引入的嗎?

我不能告訴你。 但是我們可以從源代碼中讀取,這是故意發生的,類型解析就像這樣實現。

為什么沒有記錄?

我想沒有人真的建議那樣使用進口。 Groovy非常強大,你可以用很多不同的方式做很多事情,但這並不意味着你應該這樣做。 明星導入是相當有爭議的,因為使用明星導入而不是顯式導入會使代碼更容易出錯,因為可能存在類導入沖突。 但是如果你想知道這類問題的確切答案,你就不得不問Groovy語言設計師和核心開發人員 - 他們可能毫無疑問地給你直接的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM