12

Tôi có một POJO có sử dụng một dịch vụ để làm điều gì đó:Sử dụng Groovy metaclass ghi đè lên phương pháp

public class PlainOldJavaObject { 

    private IService service; 

    public String publicMethod(String x) { 
     return doCallService(x); 
    } 

    public String doCallService(String x) { 
     if(service == null) { 
      throw new RuntimeException("Service must not be null"); 
     } 
     return service.callX(x); 
    } 

    public interface IService { 
     String callX(Object o); 
    } 
} 

Và tôi có một trường hợp thử nghiệm Groovy:

class GTest extends GroovyTestCase { 

    def testInjectedMockIFace() { 
     def pojo = new PlainOldJavaObject(service: { callX: "very groovy" } as IService) 
     assert "very groovy" == pojo.publicMethod("arg") 
    } 

    def testMetaClass() { 
     def pojo = new PlainOldJavaObject() 
     pojo.metaClass.doCallService = { String s -> 
      "no service" 
     } 
     assert "no service" == pojo.publicMethod("arg") 
    } 
} 

Phương pháp thử nghiệm đầu tiên, testInjectedMockIFace công trình như mong đợi: POJO được tạo với triển khai động IService. Khi callX được gọi, nó đơn giản trả về "rất hấp dẫn". Bằng cách này, dịch vụ được chế giễu.

Tuy nhiên tôi không hiểu tại sao phương pháp thứ hai, testMetaClass không hoạt động như mong đợi nhưng thay vào đó ném NullPointerException khi cố gắng gọi callX trên đối tượng dịch vụ. Tôi nghĩ rằng tôi đã ghi đè phương thức doCallService với dòng này:

pojo.metaClass.doCallService = { String s -> 

Tôi đang làm gì sai?

Cảm ơn!

Trả lời

16

Cú pháp của bạn hơi nhỏ. Vấn đề là pojo là một đối tượng Java và không có một metaClass.Để chặn cuộc gọi đến PlainOldJavaObject của doCallService sử dụng ExpandoMetaClass:

Chỉ cần thay thế:

pojo.metaClass.doCallService = { String s -> 
     "no service" 
    } 

Với:

PlainOldJavaObject.metaClass.doCallService = { String s -> 
     "no service" 
    } 
+3

Một điều cần lưu ý ở đây là khi bạn thao tác metaClass của Class, mọi cá thể từ điểm đó trở đi sẽ được thao tác. Điều này có thể có tác động lớn đến các thử nghiệm khác chạy trong cùng một phiên. Khi bạn thao tác một cá thể của một lớp, chỉ có cá thể đó bị ảnh hưởng. –

1

Điều bạn có vẻ ổn. Tôi chạy một phiên bản hơi sửa đổi trên nó trên webapp bảng điều khiển groovy và nó chạy mà không có vấn đề. Xem cho chính bạn bằng cách sử dụng mã này tại http://groovyconsole.appspot.com/.

public interface IService { 
    String callX(Object o); 
} 

public class PlainOldJavaObject { 

    private IService service; 

    public String publicMethod(String x) { 
     return doCallService(x); 
    } 

    public String doCallService(String x) { 
     if(service == null) { 
      throw new RuntimeException("Service must not be null"); 
     } 
     return service.callX(x); 
    } 
} 

def pojo = new PlainOldJavaObject() 
pojo.metaClass.doCallService = { String s -> 
    "no service" 
} 
println pojo.publicMethod("arg") 

Phiên bản Groovy nào bạn đang sử dụng. Nó có thể là một lỗi trong Groovy trong việc thực hiện metaclass. Ngôn ngữ groovy di chuyển khá nhanh và việc thực hiện metaclass thay đổi từ phiên bản này sang phiên bản khác.

Chỉnh sửa - Phản hồi từ Nhận xét:

Phiên bản của webapp bảng điều khiển groovy là 1.7-rc-1. Vì vậy, có vẻ như phiên bản đó có thể hoạt động như bạn muốn. Họ hiện đang ở RC2 vì vậy tôi hy vọng nó sẽ được phát hành sớm. Không chắc chắn nếu những gì bạn đang nhìn thấy là một lỗi hoặc chỉ là một sự khác biệt trong cách nó hoạt động trong phiên bản 1.6.x.

+0

Hi Chris, tôi chạy Groovy Version: 1.6.5 JVM: 1.6.0_13 – raoulsson

+0

Phiên bản không có gì để làm với nó. Vấn đề là doCallService (x) là mã Java, không phải mã Groovy, vì vậy nó không phải là nhận thức metaClass. – noah

15

Nếu POJO của bạn thực sự là một lớp Java, chứ không phải là một lớp Groovy, thì đó là vấn đề của bạn. Các lớp Java không gọi các phương thức thông qua metaClass. ví dụ, trong Groovy:

pojo.publicMethod('arg') 

tương đương với Java này:

invokeMethod gửi các cuộc gọi thông qua metaclass. Nhưng phương pháp này:

public String publicMethod(String x) { 
    return doCallService(x); 
} 

là một phương pháp Java. Nó không sử dụng invokeMethod để gọi doCallService. Để mã của bạn hoạt động, PlainOldJavaObject cần phải là một lớp Groovy để tất cả các cuộc gọi sẽ đi qua metaClass. Mã Java thông thường không sử dụng metaClasses.

Tóm lại: ngay cả Groovy cũng không thể ghi đè các cuộc gọi phương thức Java, nó chỉ có thể ghi đè các cuộc gọi từ Groovy hoặc gửi đi qua invokeMethod.

+0

Làm cách nào để phân biệt đúng mã Groovy và mã Java? Làm thế nào bạn sẽ làm cho PlainOldGroovyObject thay vì PlainOldJavaObject? – Tomato

+4

Nếu nó nằm trong tệp .groovy, đó là một lớp Groovy. – noah

+0

đã nhận được. cảm ơn để làm rõ :) – Tomato

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