Nhiều chú thích cùng loại trên một phần tử?


91

Tôi đang cố gắng bổ sung hai hoặc nhiều chú thích cùng loại vào một phần tử, trong trường hợp này là một phương thức. Đây là mã gần đúng mà tôi đang làm việc:

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ề chú thích trùng lặp:

max @ upsight: ~ / work / daybreak $ javac Dupe.java 
Dupe.java:5: trùng lặp chú thích

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

Nếu điều trên không thể thực hiện được, thì một số cách giải quyết tiềm năng là gì?

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

Tôi đang xây dựng một cơ chế sai cú pháp để "ánh xạ" POJO đến các kho lưu trữ tài liệu như MongoDB. Tôi muốn cho phép các chỉ mục được chỉ định dưới dạng chú thích trên getters hoặc setters. Đây là một ví dụ giả định:

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 phiên bản của Nhân viên bằng 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 thực hiện phương pháp được chỉ định trong câu trả lời được chấp nhận. Mặc dù Hibernate thực hiện điều này và nó không bị coi là hack, nhưng tôi nghĩ rằng nó vẫn hợp lý khi ít nhất cho phép có nhiều chú thích cùng loại trên một phần tử duy nhất.


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

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.
Tối đa A.

Nó có thể hữu ích trong CDI để cho phép cung cấp một hạt đậu cho nhiều vòng loại. Ví dụ tôi đã chỉ cố gắng để tái sử dụng một bean ở hai nơi bằng đủ điều kiện nó "@Produces @PackageName (" test1 ") @PackageName (" test2 ")"
Richard Corfield

Hơn nữa: Câu trả lời bên dưới không giải quyết được vấn đề đó bởi vì CDI sẽ coi tổng hợp là một Vòng loại.
Richard Corfield

@MaxA. Vui lòng thay đổi chấp nhận "séc xanh" của bạn thành Câu trả lời đúng của mernst. Java 8 đã thêm khả năng lặp lại các chú thích.
Basil Bourque

Câu trả lời:


140

Không cho phép hai hoặc nhiều chú thích cùng loại. Tuy nhiên, bạn có thể làm như sau:

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

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

Tuy nhiên, bạn sẽ cần xử lý chuyên dụng chú thích Foos trong mã.

btw, tôi vừa mới sử dụng cái này 2 giờ trước để giải quyết vấn đề tương tự :)


2
Bạn cũng có thể làm điều này ở Groovy?
Excel 20

5
@ Excel20 Có. Tuy nhiên, bạn sẽ phải sử dụng dấu ngoặc vuông, ví dụ @Foos([@Foo(bar="one"), @Foo(bar="two")]). Xem groovy.codehaus.org/Annotations+with+Groovy
sfussenegger

Hơi muộn trong ngày nhưng quan tâm đến lời khuyên có thể xử lý danh sách Foo bên trong Foos không? Hiện nay tôi đang cố gắng để prcess kết quả của một phương pháp nhưng mặc dù Foos được chặn những lời khuyên Foo không bao giờ bước vào
Stelios Koussouris

Cập nhật: Kể từ Java 8, Câu trả lời này đã lỗi thời. Xem Câu trả lời hiện đại chính xác của mernst. Java 8 đã thêm khả năng lặp lại các chú thích.
Basil Bourque


21

Ngoài những cách khác đã đề cập, có một cách ít dài dòng hơn 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ụ theo mặc định được, FooContainerdướ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 bản in trên:

@ 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)])


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

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

16

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

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

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

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

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

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

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

12

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

Giải pháp thông thường là xây dựng một 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 (Hibernate chẳng hạn), vì vậy nó không nên được coi là một hack, mà là một giải pháp chính xác cho nhu cầu này.


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

Thí dụ:

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

Tôi biết điều này quen thuộc một cách kỳ lạ. Cảm ơn vì đã làm mới bộ nhớ của tôi.
Tối đa A.

Bây giờ có thể với Java 8.
Anis

4

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

@Foos({"one","two"})
private String awk;

//...

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

3

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

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

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


Điểm đúng, nhưng làm thế nào để cố gắng trả lời câu hỏi của OP?
Mordechai

@MouseEvent bạn nói đú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 nhiều hơn trong các nhận xét ở đó. Nhưng câu trả lời là lỗi thời nào do chú thích lặp lại ...
golwig

0

Trong phiên bản Java hiện tại, tôi có thể giải quyết vấn đề này bằng chú thích sau:

@Foo({"one", "two"})

1
phiên bản nào? jdk 8,9,10,11?
Gaurav
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.