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.


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 :)


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


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() {}

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();
    }

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();
}

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 ..


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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.