2015-11-17 11 views
6

Tôi thường khuyên bạn nên chuyển đổi AST @Immutable của Groovy như một cách dễ dàng để tạo các lớp, tốt, không thay đổi. Điều này luôn hoạt động tốt với các lớp Groovy khác, nhưng ai đó gần đây đã hỏi tôi liệu tôi có thể trộn các lớp đó vào mã Java hay không. Tôi luôn luôn nghĩ rằng câu trả lời là có, nhưng tôi đánh một tiếng.Các lớp Groovy @Immutable trong Java

Nói rằng tôi có một bất biến User lớp:

import groovy.transform.Immutable 

@Immutable 
class User { 
    int id 
    String name 
} 

Nếu tôi thử nghiệm này sử dụng một thử nghiệm JUnit viết bằng Groovy, mọi thứ hoạt động như mong đợi:

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' 
    } 
} 

tôi có thể làm tương tự với một Kiểm tra JUnit được viết bằng Java:

nhập 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 
    } 
} 

Công trình này, mặc dù có sự cố trên đường chân trời. IntelliJ 15 không thích cuộc gọi đến new User(), tuyên bố rằng không tìm thấy hàm tạo. Điều đó cũng có nghĩa là IDE nhấn mạnh lớp màu đỏ, nghĩa là nó có lỗi biên dịch. Tuy nhiên, bài kiểm tra vẫn trôi qua, hơi lạ một chút, nhưng cũng vậy.

Nếu tôi cố gắng sử dụng trực tiếp lớp User trong mã Java, mọi thứ bắt đầu trở nên lạ.

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

Một lần nữa IntelliJ không vui, nhưng biên dịch và chạy. Kết quả là, tất cả mọi thứ:

User(0) 

Đó là kỳ quặc, bởi vì mặc dù @Immutable chuyển đổi không tạo ra một phương pháp toString, tôi khá mong đợi nó đầu ra để hiển thị cả tài sản. Tuy nhiên, điều đó có thể là do thuộc tính name là không, vì vậy nó không được bao gồm trong đầu ra.

Nếu tôi cố gắng sử dụng các nhà xây dựng tuple:

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

tôi nhận được

User(0, name) 

như đầu ra, ít nhất là thời gian này (đôi khi nó không hoạt động ở tất cả).

Sau đó, tôi đã thêm tệp xây dựng Gradle. Nếu tôi đặt các lớp Groovy dưới src\main\groovy và các lớp Java dưới src\main\java (tương tự cho các bài kiểm tra nhưng sử dụng thư mục test thay), ngay lập tức tôi nhận được một vấn đề biên soạn:

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

tôi thường sửa chữa các vấn đề xuyên biên soạn như thế này bằng cách cố gắng sử dụng trình biên dịch Groovy cho mọi thứ. Nếu tôi đặt cả hai lớp dưới src\main\java, không có gì thay đổi, đó không phải là một bất ngờ lớn. Nhưng nếu tôi đặt cả hai lớp dưới src\main\groovy, sau đó tôi có được điều này trong giai đoạn 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 

Huh. Lần này nó phản đối constructor tuple, bởi vì nó nghĩ rằng nó chỉ có một constructor mặc định. Tôi biết phép biến đổi thêm một giá trị mặc định, một trình xây dựng dựa trên bản đồ và một bộ tạo tuple, nhưng có lẽ chúng không được tạo ra kịp thời cho mã Java để xem chúng.

Ngẫu nhiên, nếu tôi tách Java và lớp Groovy một lần nữa, và thêm dòng sau vào Gradle tôi xây dựng:

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

tôi nhận được lỗi tương tự. Nếu tôi không thêm khối sourceSets, tôi sẽ gặp lỗi không tìm thấy lớp học User từ trước đó.

Vì vậy, dòng dưới cùng là, cách chính xác để thêm một lớp học @Immutable Groovy vào một hệ thống Java hiện có là gì? Có cách nào để có được các nhà xây dựng được tạo ra trong thời gian cho Java để xem chúng?

Tôi đã tạo bản trình bày Groovy cho các nhà phát triển Java trong nhiều năm và nói rằng bạn có thể làm điều này, chỉ để bây giờ gặp phải các vấn đề. Xin hãy giúp tôi cứu mặt bằng cách nào đó. :)

+0

IDE có xu hướng đưa ra âm tính sai khi nói đến Groovy, đặc biệt là với các biến đổi AST. Đối với vấn đề biên dịch chéo bạn có thể thử có hai dự án riêng biệt, một java và một groovy. Nếu đây vẫn là một câu hỏi chưa được giải quyết, tôi sẽ cho nó một tiếng huýt sáo vào buổi sáng. Điều này thực sự cảm thấy như nó sẽ làm việc. – cjstehno

+0

Hành vi có giống với '@ CompileStatic' không? – dmahapatro

+0

'@ CompileStatic' không thay đổi bất cứ điều gì – kousen

Trả lời

4

Tôi cũng đã thử kịch bản của bạn, nơi bạn có một dự án với một số src/main/java và một thư mục src/main/groovy và kết thúc bằng các lỗi biên dịch tương tự như những gì bạn thấy.

Tôi đã có thể sử dụng các đối tượng bất biến Groovy trong Java khi tôi đặt các bất biến Groovy trong một dự án riêng biệt từ mã Java. Tôi đã tạo một ví dụ đơn giản và đẩy nó vào GitHub (https://github.com/cjstehno/immut).

Về cơ bản đó là một dự án đa cấp Gradle với tất cả mã Groovy (đối tượng không thay đổi) trong tiểu dự án immut-groovy và tất cả mã Java trong dự án immut-java. Dự án immut-java phụ thuộc vào dự án immut-groovy và sử dụng các đối tượng bất biến Something:

public class SomethingFactory { 

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

Tôi đã thêm một thử nghiệm đơn vị trong dự án Java mà tạo ra một thể hiện mới của lớp Groovy bất biến và xác minh nội dung của nó.

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

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

Điều này không thực sự lý tưởng, nhưng nó hoạt động.

Các vấn đề liên quan