2015-11-17 34 views
6

我經常推薦Groovy的@Immutable AST轉換,它是一種簡單的方法來創建類,它是不可變的。對於其他Groovy類,這總是可以正常工作,但最近有人問我是否可以將這些類混合到Java代碼中。我一直認爲答案是肯定的,但我遇到了困難。Groovy @ Java中的不可變類

說我有一個不變的User類:

import groovy.transform.Immutable 

@Immutable 
class User { 
    int id 
    String name 
} 

如果我測試這個使用Groovy編寫的JUnit測試,一切正常:

import org.junit.Test 

class UserGroovyTest { 

    @Test 
    void testMapConstructor() { 
     assert new User(name: 'name', id: 3) 
    } 

    @Test 
    void testTupleConstructor() { 
     assert new User(3, 'name') 
    } 

    @Test 
    void testDefaultConstructor() { 
     assert new User() 
    } 

    @Test(expected = ReadOnlyPropertyException) 
    void testImmutableName() { 
     User u = new User(id: 3, name: 'name') 
     u.name = 'other' 
    } 
} 

我可以做同樣的用用Java編寫的JUnit測試:

import static org.junit.Assert。*;

import org.junit.Test; 

public class UserJavaTest { 

    @Test 
    public void testDefaultCtor() { 
     assertNotNull(new User()); 
    } 

    @Test 
    public void testTupleCtor() { 
     assertNotNull(new User(3, "name")); 
    } 

    @Test 
    public void testImmutableName() { 
     User u = new User(3, "name"); 
     // u.setName("other") // Method not found; doesn't compile 
    } 
} 

雖然在地平線上有麻煩, IntelliJ 15不喜歡調用new User(),聲稱沒有找到構造函數。這也意味着IDE用紅色強調該類,這意味着它有一個編譯錯誤。無論如何,測試都會通過,這有點奇怪,但這樣做。

如果我嘗試直接在Java代碼中使用User類,事情會變得很奇怪。

public class UserDemo { 
    public static void main(String[] args) { 
     User user = new User(); 
     System.out.println(user); 
    } 
} 

再次IntelliJ不開心,但編譯和運行。輸出是,所有的東西:

User(0) 

這是奇怪的,因爲雖然@Immutable變換不會產生toString方法,我寧願希望它的輸出同時顯示性能。不過,這可能是因爲name屬性爲空,所以它不包含在輸出中。

如果我嘗試使用元組構造:

public class UserDemo { 
    public static void main(String[] args) { 
     User user = new User(3, "name"); 
     System.out.println(user); 
    } 
} 

我得到

User(0, name) 

爲輸出,至少在這個時候(有時不工作在所有)。

然後我添加了一個Gradle構建文件。如果我把src\main\groovy下,Groovy類和Java類src\main\java(同爲測試,但使用test文件夾代替)下,我立即得到一個編譯問題:

> gradle test 
error: cannot find symbol 
User user = new User(...) 
^ 

我通常解決這樣的交叉編譯的問題通過嘗試使用Groovy編譯器來處理所有事情。如果我把這兩門課都放在src\main\java之下,沒有什麼變化,這並不是什麼大驚喜。但是,如果我把src\main\groovy下的兩個類,然後我在compileGroovy階段得到這樣的:

> gradle clean test 
error: constructor in class User cannot be applied to the given types; 
User user = new User(3, "name"); 

required: no arguments 
found: int,String 
reason: actual and formal arguments differ in length 

呵呵。這一次它反對元組構造函數,因爲它認爲它只有一個默認的構造函數。我知道這個轉換添加了一個默認的,一個基於地圖的和一個元組構造器,但也許他們沒有及時生成Java代碼來查看它們。

順便說一句,如果我再次分開Java和Groovy類,並添加以下到我的搖籃建設:

sourceSets { 
    main { 
     java { srcDirs = []} 
     groovy { srcDir 'src/main/java' } 
    } 
} 

我得到同樣的錯誤。如果我不添加sourceSets塊,則會收到User類未找到的錯誤。

因此底線是,將Groovy類添加到現有Java系統的正確方法是什麼?有沒有辦法讓Java的構造函數能夠及時生成以便看到它們?

多年來,我一直在向Java開發人員進行Groovy演示,並說您可以這樣做,但現在只會遇到問題。莫名其妙地幫助我保存臉部。 :)

+0

當談到Groovy時,IDE傾向於給出假陰性,尤其是AST轉換。對於交叉編譯問題,你可以嘗試有兩個獨立的項目,一個是java和一個是groovy。如果這仍然是一個尚未解決的問題,我會在早上給它一個旋風。這真的覺得它應該工作。 – cjstehno

+0

行爲與'@ CompileStatic'相同嗎? – dmahapatro

+0

'@ CompileStatic'不會改變任何東西 – kousen

回答

4

我也嘗試過你的場景,你有一個單獨的項目,其中有一個src/main/java和一個src/main/groovy目錄,最後出現類似於你所看到的編譯錯誤。

當我將Groovy不可變對象放入Java代碼的單獨項目中時,我能夠在Java中使用Groovy不可變對象。我創建了一個簡單的例子,並將其推送到GitHub(https://github.com/cjstehno/immut)。

基本上它是一個Gradle多項目,包含immut-groovy子項目中的所有Groovy代碼(不可變對象)以及immut-java項目中的所有Java代碼。該項目immut-java依賴於immut-groovy項目和使用不可變對象Something

public class SomethingFactory { 

    Something createSomething(int id, String label){ 
     return new Something(id, label); 
    } 
} 

我在其中創建不可改變的Groovy類的新實例,並驗證其內容的Java項目增加了一個單元測試。

public class SomethingFactoryTest { 
    @Test 
    public void createSomething(){ 
     Something something = new SomethingFactory().createSomething(42, "wonderful"); 

     assertEquals(something.getId(), 42); 
     assertEquals(something.getLabel(), "wonderful"); 
    } 
} 

這並不是很理想,但它的工作原理。