Beberapa anotasi berjenis sama pada satu elemen?

91

Saya mencoba menampar dua atau lebih penjelasan dari jenis yang sama pada satu elemen, dalam hal ini, metode. Berikut perkiraan kode yang saya kerjakan:

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

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

Saat menyusun penjelasan di atas, javac mengeluh tentang anotasi duplikat:

max @ upsight: ~ / work / daybreak $ javac Dupe.java 
Dupe.java:5: anotasi duplikat

Apakah tidak mungkin mengulangi penjelasan seperti ini? Ngomong-ngomong, bukankah kedua contoh @Foo di atas berbeda karena isinya berbeda?

Jika hal di atas tidak memungkinkan, apa sajakah solusi potensial?

UPDATE: Saya telah diminta untuk menjelaskan kasus penggunaan saya. Ini dia.

Saya sedang membangun mekanisme sintaksis untuk "memetakan" POJO ke penyimpanan dokumen seperti MongoDB. Saya ingin mengizinkan indeks ditetapkan sebagai anotasi pada pengambil atau penyetel. Inilah contoh yang dibuat-buat:

public class Employee {
    private List<Project> projects;

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

Jelas, saya ingin dapat dengan cepat menemukan contoh Karyawan berdasarkan berbagai properti Project. Saya dapat menentukan @Index dua kali dengan nilai expr () yang berbeda, atau mengambil pendekatan yang ditentukan dalam jawaban yang diterima. Meskipun Hibernate melakukan ini dan itu tidak dianggap sebagai peretasan, saya pikir masih masuk akal untuk setidaknya mengizinkan memiliki beberapa anotasi dengan jenis yang sama pada satu elemen.

Max A.
sumber
1
Ada upaya untuk membuat aturan duplikat ini santai untuk mengizinkan program Anda di Java 7. Bisakah Anda menjelaskan kasus penggunaan Anda?
notnoop
Saya telah mengedit pertanyaan saya dengan deskripsi mengapa saya ingin melakukan ini. Terima kasih.
Max A.
Ini bisa berguna dalam CDI untuk memungkinkan kacang disediakan untuk beberapa kualifikasi. Misalnya, saya baru saja mencoba menggunakan kembali kacang di dua tempat dengan mengkualifikasinya "@Produces @PackageName (" test1 ") @PackageName (" test2 ")"
Richard Corfield
Lebih lanjut: Jawaban di bawah ini tidak menyelesaikan masalah tersebut karena CDI akan melihat komposit sebagai satu Qualifier.
Richard Corfield
@Majelisbasaudan Silakan ubah penerimaan "centang hijau" Anda menjadi Jawaban yang benar oleh mernst. Java 8 menambahkan kemampuan untuk mengulang anotasi.
Basil Bourque

Jawaban:

140

Dua atau lebih anotasi dengan jenis yang sama tidak diperbolehkan. Namun, Anda bisa melakukan sesuatu seperti ini:

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

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

Anda akan membutuhkan penanganan khusus untuk anotasi Foos dalam kode.

btw, saya baru saja menggunakan ini 2 jam yang lalu untuk mengatasi masalah yang sama :)

sfussenegger.dll
sumber
2
Bisakah Anda juga melakukan ini di Groovy?
Excel20
5
@ Excel20 Ya. Anda harus menggunakan tanda kurung siku, misalnya @Foos([@Foo(bar="one"), @Foo(bar="two")]). Lihat groovy.codehaus.org/Annotations+with+Groovy
sfussenegger
Agak terlambat di hari itu, tetapi peduli untuk menunjukkan saran yang dapat memproses daftar Foo di dalam Foos? Saat ini saya mencoba memproses hasil dari suatu metode tetapi meskipun Foos dicegat, nasihat Foo tidak pernah dimasukkan
Stelios Koussouris
Pembaruan: Pada Java 8, Jawaban ini sudah ketinggalan zaman. Lihat Jawaban modern yang benar oleh mernst. Java 8 menambahkan kemampuan untuk mengulang anotasi.
Basil Bourque
21

Terlepas dari cara-cara lain yang disebutkan, ada satu lagi cara yang lebih sedikit di 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{

}

Contoh secara default mendapat, FooContainersebagai Annotation

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

Kedua cetakan di atas:

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

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

Jatin
sumber
Perlu diperhatikan bahwa metode / nama kolom di FooContainer harus memiliki nama "value ()" yang ketat. Jika tidak, Foo tidak akan bisa dikompilasi.
Tomasz Mularczyk
... juga berisi jenis anotasi (FooContainer) tidak dapat diterapkan ke lebih banyak jenis daripada jenis anotasi berulang (Foo).
Tomasz Mularczyk
16

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

Mulai dari Java8, Anda dapat mendeskripsikan anotasi berulang:

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

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

Catatan, valuebidang wajib diisi untuk daftar nilai.

Sekarang Anda dapat menggunakan anotasi yang mengulanginya daripada mengisi array:

@Foo(bar="one")
@Foo(bar="two")
public void haha() {}
Sergei Pikalev
sumber
12

Seperti yang dikatakan oleh sfussenegger, ini tidak mungkin.

Solusi yang biasa adalah membuat anotasi "multipel" , yang menangani larik anotasi sebelumnya. Ini biasanya dinamai sama, dengan akhiran 's'.

Ngomong-ngomong, ini sangat digunakan dalam proyek publik besar (Hibernate misalnya), jadi tidak boleh dianggap sebagai peretasan, melainkan solusi yang tepat untuk kebutuhan ini.


Bergantung pada kebutuhan Anda, akan lebih baik jika anotasi Anda sebelumnya menangani beberapa nilai .

Contoh:

    public @interface Foo {
      String[] bars();
    }
KLE
sumber
Saya tahu ini sangat familiar. Terima kasih telah menyegarkan ingatan saya.
Max A.
Sekarang mungkin dengan Java 8.
Anis
4

menggabungkan jawaban lain ke dalam bentuk yang paling sederhana ... anotasi dengan daftar nilai sederhana ...

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

//...

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

Jika Anda hanya memiliki 1 parameter "bar", Anda dapat menamainya sebagai "nilai". Dalam hal ini Anda tidak perlu menulis nama parameter sama sekali saat Anda menggunakannya seperti ini:

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

sedikit lebih pendek dan lebih rapi, imho ..

golwig
sumber
Poin yang benar, tapi bagaimana cara ini untuk menjawab pertanyaan OP?
Mordechai
@MouseEvent Anda benar, saya pikir itu lebih merupakan peningkatan dari jawaban teratas dari sfussenegger dan dengan demikian lebih termasuk dalam komentar di sana. Tapi jawabannya sudah ketinggalan zaman karena anotasi yang berulang ...
golwig
0

Di versi Java saat ini, saya dapat menyelesaikan masalah ini dengan anotasi berikut:

@Foo({"one", "two"})
Eduardo Alexandre de Souza
sumber
1
versi yang mana jdk 8,9,10,11?
Gaurav