Groovy StringBuilder類踩坑

今天在寫腳本的時候發現一個奇怪的錯誤。經過猜想驗證,發現原來Groovy過於靈活了,算是重複踩了之前的坑。Groovy特性描述如下:

當Groovy腳本調用getFun()和setFun()方法時,會默認給這個類有一個FunTester的屬性。反過來,如果這個類有Fun這個屬性,那麼get和set方法是不用顯式寫出來的。

本來這是個好事情,一方面其實可以免去多餘代碼,一方面更容易收攏入口方法(因爲直接訪問屬性、修改屬性也是有限調用get和set方法)。

但是就是這個特性讓我下面的代碼報錯了。

        StringBuilder sb = new StringBuilder()
        for (int i = 0; i < 3; i++) {
            sb.append("a" + i)
            output(sb.toString())
            sb.setLength(0)
        }

其中java.lang.AbstractStringBuilder#setLength方法是用來裁剪StringBuilder對象內容的,如果參數爲0,實際的功效就是清空。

官方的描述爲:

setLength(int newLength)AbstractStringBuilder 類中的一個方法,用於設置字符串的長度。具體來說,它有以下作用:

  • 如果 newLength 小於當前字符串的長度,則字符串將被截斷爲指定的長度 newLength
  • 如果 newLength 大於當前字符串的長度,則在字符串末尾填充空字符('\0')直到字符串達到指定的長度 newLength

下面是我代碼報錯信息:

No signature of method: java.lang.StringBuilder.setLength() is applicable for argument types: (Integer) values: [0]
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:70)
	at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)

錯誤信息顯示並沒有這個方法,真是見了鬼了,我在Intellij裏面查看源碼時明明顯示沒有問題。當我把問題拋給ChatGPT,得到如下回答。

這個錯誤信息是Groovy中的一個問題,可能是因爲Groovy與Java在StringBuilder類的操作上存在某些不同。setLength()方法通常用於設置StringBuilder的長度,但是錯誤信息表明setLength()方法似乎被錯誤地調用,傳遞了一個不正確的參數類型。在Java中,setLength()方法接受一個int類型的參數,表示要設置的新長度。但是在錯誤信息中顯示傳遞了Integer類型的值 [0],這可能導致方法調用失敗。如果問題仍然存在,可能需要進一步檢查Groovy代碼中的其他部分,以確保在StringBuilder操作方面沒有其他的語法錯誤或問題。

這個時候我才意識到這是一個Groovy的報錯,並不是Java的。所以,根據我多年踩坑經驗早就的敏感度,set方法一定是出問題了。經過我檢查,StringBuilder類並沒有length屬性,這就是導致報錯的根本原因。

下面我來驗證自己的猜想,通過Groovy元編程給StringBuilder類加上這個length屬性。

        StringBuilder.metaClass.length= 32
        StringBuilder sb = new StringBuilder()
        for (int i = 0; i < 3; i++) {
            sb.append("a" + i)
            output(sb.toString())
            sb.setLength(0)
        }

這下不報錯了,但問題來了,setLength功能不起作用了,因爲優先去設置屬性值去了。看來雖然驗證了,但是功能破壞了,只好用點笨辦法了。

        StringBuilder.metaClass.length= 32
        StringBuilder sb = new StringBuilder()
        for (int i = 0; i < 3; i++) {
            sb.append("a" + i)
            output(sb.toString())
        }

這個就解決了所有問題。當我去用Java代碼中驗證時,發現一直沒有報錯。我換了個項目(Maven/Gradle)結果發現居然無法復現了。哎,又遇到幽靈的問題,可能Groovy在編譯這個項目類的時候開小差了。我的JDK版本17,Groovy編譯插件版本3.0.1,重新清空本地緩存重啓Intellij也依然如此。
通過對比兩個項目差異,同時升級Groovy依賴版本和編譯插件版本,改缺陷自動解決了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章