2009-10-12 40 views
75

Tôi đang cố gắng tát hai hoặc nhiều chú thích cùng loại trên một phần tử, trong trường hợp này là một phương pháp. Dưới đây là các mã xấp xỉ mà tôi đang làm việc với:Nhiều chú thích cùng loại trên một phần tử?

public class Dupe { 
    public @interface Foo { 
     String bar(); 
    } 

    @Foo(bar="one") 
    @Foo(bar="two") 
    public void haha() {} 
} 

Khi biên dịch trên, javac phàn nàn về một chú thích trùng lặp:

 
[email protected]:~/work/daybreak$ javac Dupe.java 
Dupe.java:5: duplicate annotation 

Có đơn giản là không thể lặp lại chú thích như thế này? Nói một cách dã man, không phải là hai trường hợp @Foo ở trên khác nhau do nội dung của chúng khác nhau không?

Nếu không thể nói ở trên, một số cách giải quyết tiềm ẩn là gì?

CẬP NHẬT: Tôi đã được yêu cầu mô tả trường hợp sử dụng của tôi. Đây rồi.

Tôi đang xây dựng cơ chế đường cú pháp để "lập bản đồ" POJO để lưu trữ các cửa hàng như MongoDB. Tôi muốn cho phép chỉ mục được chỉ định dưới dạng chú thích trên getters hoặc setters. Dưới đây là ví dụ giả tạo:

public class Employee { 
    private List<Project> projects; 

    @Index(expr = "project.client_id") 
    @Index(expr = "project.start_date") 
    public List<Project> getProjects() { return projects; } 
} 

Rõ ràng, tôi muốn có thể nhanh chóng tìm thấy các trường hợp Nhân viên theo các thuộc tính khác nhau của Dự án. Tôi có thể chỉ định @Index hai lần với các giá trị expr() khác nhau hoặc sử dụng phương pháp được chỉ định trong câu trả lời được chấp nhận. Mặc dù Hibernate làm điều này và nó không được coi là một hack, tôi nghĩ rằng nó vẫn có ý nghĩa để ít nhất cho phép có nhiều chú thích cùng loại trên một yếu tố duy nhất.

+1

có một nỗ lực để có quy tắc trùng lặp này thư giãn để cho phép chương trình của bạn trong Java 7. Bạn có thể mô tả trường hợp sử dụng của bạn không? – notnoop

+0

Tôi đã chỉnh sửa câu hỏi của mình với mô tả lý do tại sao tôi muốn làm điều này. Cảm ơn. –

+0

Có thể thuận tiện trong CDI để cho phép đậu được cung cấp cho nhiều vòng loại. Ví dụ: tôi vừa thử sử dụng lại một bean ở hai nơi bằng cách đủ điều kiện "@Sản phẩm @PackageName (" test1 ") @PackageName (" test2 ")" –

Trả lời

127

Hai hoặc nhiều chú thích cùng loại không được phép. Tuy nhiên, bạn có thể làm điều gì đó như sau:

public @interface Foos { 
    Foo[] value(); 
} 

@Foos({@Foo(bar="one"), @Foo(bar="two")}) 
public void haha() {} 

Bạn sẽ cần xử lý chuyên biệt chú thích Foos trong mã mặc dù.

btw, tôi đã chỉ được sử dụng này 2 giờ trước đây để làm việc xung quanh vấn đề tương tự :)

+2

Bạn cũng có thể làm điều này trong Groovy không? – Excel20

+5

@ Excel20 Có. Bạn sẽ phải sử dụng dấu ngoặc vuông, ví dụ: '@Foos ([@ Foo (bar =" một "), @Foo (bar =" two ")])'. Xem http://groovy.codehaus.org/Annotations+with+Groovy – sfussenegger

+0

Một chút muộn trong ngày nhưng quan tâm để chỉ đến lời khuyên có thể xử lý danh sách Foo bên trong Foos? Tại thời điểm này tôi đang cố gắng để prcess kết quả của một phương pháp, nhưng mặc dù các Foos bị chặn các lời khuyên Foo là không bao giờ nhập –

12

Như đã nói bởi sfussenegger, điều này là không thể.

Giải pháp thông thường là để tạo chú thích "nhiều", xử lý một mảng của chú thích trước đó. Nó thường được đặt tên giống nhau, với hậu tố 's'.

Nhân tiện, điều này rất được sử dụng trong các dự án công cộng lớn (ví dụ như Hibernate), vì vậy nó không nên được coi là hack, mà là giải pháp đúng cho nhu cầu này.


Tùy thuộc vào nhu cầu của bạn, nó có thể là tốt hơn để cho phép chú thích trước đó của bạn để xử lý nhiều giá trị.

Ví dụ:

public @interface Foo { 
     String[] bars(); 
    } 
+0

Tôi biết điều này đã được eerily quen thuộc. Cảm ơn vì đã làm mới trí nhớ của tôi. –

+0

Giờ đây có thể với Java 8. – AnixPasBesoin

3

Nếu bạn chỉ có 1 tham số "bar", bạn có thể đặt tên cho nó là "giá trị". Trong trường hợp này, bạn sẽ không phải viết tên thông số khi bạn sử dụng nó như sau:

@Foos({@Foo("one"), @Foo("two")}) 
public void haha() {} 

ngắn hơn một chút và ngắn gọn, imho ..

+0

Điểm chính xác, nhưng làm thế nào để nỗ lực này trả lời câu hỏi của OP? – Mordechai

+0

@MouseEvent bạn đúng, tôi nghĩ rằng đó là một cải tiến của câu trả lời hàng đầu từ sfussenegger và do đó thuộc về nhiều hơn trong các ý kiến ​​lên đó. Nhưng câu trả lời là lỗi thời vì các chú thích có thể lặp lại ... – mihahh

3

kết hợp các câu trả lời khác thành dạng đơn giản nhất ... một chú thích với một danh sách đơn giản của các giá trị ...

11

http://docs.oracle.com/javase/tutorial/java/annotations/repeating.html

Bắt đầu từ Java8 bạn có thể mô tả chú thích lặp lại:

@Repeatable(FooValues.class) 
public @interface Foo { 
    String bar(); 
} 

public @interface FooValues { 
    Foo[] value(); 
} 

Lưu ý, value là trường bắt buộc cho danh sách giá trị.

Bây giờ bạn có thể sử dụng chú thích lặp đi lặp lại cho họ thay vì làm đầy các mảng:

@Foo(bar="one") 
@Foo(bar="two") 
public void haha() {} 
9

Ngoài những cách khác được đề cập, có thêm một cách ít tiết trong Java8:

@Target(ElementType.TYPE) 
@Repeatable(FooContainer.class) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Foo { 
    String value(); 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface FooContainer { 
     Foo[] value(); 
     } 

@Foo("1") @Foo("2") @Foo("3") 
class Example{ 

} 

Ví dụ bởi mặc định được, FooContainer dưới dạng chú thích

Arrays.stream(Example.class.getDeclaredAnnotations()).forEach(System.out::println); 
    System.out.println(Example.class.getAnnotation(FooContainer.class)); 

Cả hai trên in:

@ com.FooContainer (value = [@ com.Foo (value = 1), @ com.Foo (value = 2), @ com.Foo (value = 3)])

@ com.FooContainer (value = [@ com.Foo (value = 1), @ com.Foo (value = 2), @ com.Foo (value = 3)])

+0

Điều đáng nói là phương thức/tên trường trong FooContainer phải được đặt tên nghiêm ngặt là "value()". Nếu không thì Foo sẽ không biên dịch. – Tomasz

+0

... cũng chứa loại chú thích (FooContainer) không thể áp dụng cho nhiều loại hơn loại chú thích có thể lặp lại (Foo). – Tomasz

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