[英]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/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);
}
}
我在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.