簡體   English   中英

Java中的Groovy @Immutable類

[英]Groovy @Immutable classes in Java

我經常建議使用Groovy的@Immutable AST轉換作為使類良好@Immutable的簡單方法。 這在其他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測試執行相同的操作:

導入靜態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屬性為null,所以它不包含在輸出中。

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

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

我懂了

User(0, name)

作為輸出,至少這次(有時根本不起作用)。

然后我添加了Gradle構建文件。 如果將Groovy類放在src\\main\\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類,並將以下內容添加到我的Gradle構建中:

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

我犯了同樣的錯誤。 如果不添加sourceSets塊, sourceSets收到來自較早版本的User class not found錯誤。

因此,最重要的是,將@Immutable Groovy類添加到現有Java系統的正確方法是什么? 有什么方法可以讓Java及時看到構造函數嗎?

多年來,我一直在向Java開發人員進行Groovy演示,並說您可以做到這一點,直到現在遇到問題。 請以某種方式幫助我節省面子。 :)

我也嘗試了您的方案,在該方案中,您有一個帶有src/main/javasrc/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);
    }
}

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

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

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

這不是很理想,但是可以。

暫無
暫無

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

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