Saya diajari bahwa antarmuka Marker di Java adalah antarmuka kosong dan digunakan untuk memberi sinyal kepada compiler atau JVM bahwa objek kelas yang mengimplementasikan antarmuka ini harus diperlakukan dengan cara khusus, seperti serialisasi, kloning, dll.
Tapi belakangan ini saya belajar bahwa sebenarnya tidak ada hubungannya dengan compiler atau JVM. Misalnya, dalam kasus Serializable
antarmuka metode writeObject(Object)
dari ObjectOutputStream
melakukan sesuatu seperti instanceOf Serializable
untuk mendeteksi apakah mengimplementasikan kelas Serializable
& melempar NotSerializableException
sesuai. Semuanya ditangani dalam kode dan ini tampaknya menjadi pola desain jadi saya pikir kita bisa mendefinisikan antarmuka penanda kita sendiri.
Sekarang keraguan saya:
Apakah definisi antarmuka marker yang disebutkan di atas pada poin pertama salah? Bagaimana kita bisa mendefinisikan antarmuka Marker?
Dan alih-alih menggunakan
instanceOf
operator, mengapa metodenya tidak bisa seperti ituwriteObject(Serializable)
sehingga ada pemeriksaan jenis waktu kompilasi daripada runtime?Bagaimana Anotasi lebih baik daripada Antarmuka Penanda?
sumber
Serializable
karena Anotasi tidak masuk akal dan@NonNull
sebagai Antarmuka tidak masuk akal. Saya akan mengatakan: Anotasi adalah Penanda + Metadata. BTW: Pelopor Anotasi adalah XDoclet, lahir di Javadoc, dibunuh oleh Anotasi.Jawaban:
writeObject(Serializable)
sehingga ada pemeriksaan jenis waktu kompilasi - Ini memungkinkan Anda menghindari pencemaran kode Anda dengan nama antarmuka penanda saat "biasaObject
" diperlukan. Misalnya, jika Anda membuat kelas yang perlu dibuat bersambung, dan memiliki anggota objek, Anda akan dipaksa untuk melakukan transmisi atau membuat objekSerializable
pada waktu kompilasi. Ini tidak nyaman, karena antarmukanya tidak memiliki fungsi apa pun.sumber
writeObject
bersifat pribadi, itu berarti bahwa misalnya kelas tidak harus memanggil implementasi superclassCloneable
ini tidak jelas apakah itu perpustakaan atau perlakuan JVM dari instance yang diubah.Hal ini tidak mungkin untuk menegakkan
Serializable
ataswriteObject
karena anak-anak kelas non-Serializable dapat serializable, tapi kasus mereka mungkin upcasted kembali ke kelas induk. Akibatnya, memegang referensi ke sesuatu yang tidak dapat diserialkan (sepertiObject
) tidak berarti bahwa contoh yang dirujuk tidak dapat benar-benar berseri. Misalnya dalamObject x = "abc"; if (x instanceof Serializable) { }
kelas induk (
Object
) tidak dapat diserialisasi dan akan dijalankan menggunakan konstruktor tanpa parameternya. Nilai yang direferensikan olehx
,,String
dapat bersambung dan pernyataan bersyarat akan dijalankan.sumber
Saya telah membuat demonstrasi sederhana untuk menyelesaikan keraguan no 1 dan 2:
Kami akan memiliki antarmuka Movable yang akan diimplementasikan oleh
MobilePhone.java
Kelas dan satu kelas lagiLandlinePhone.java
yang TIDAK menerapkan antarmuka MovableAntarmuka penanda kami:
package com; public interface Movable { }
LandLinePhone.java
danMobilePhone.java
package com; class LandLinePhone { // more code here } class MobilePhone implements Movable { // more code here }
Kelas Pengecualian Kustom kami: package com;
public class NotMovableException extends Exception { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "this object is not movable"; } // more code here }
Kelas Tes kami:
TestMArkerInterface.java
package com; public class TestMarkerInterface { public static void main(String[] args) throws NotMovableException { MobilePhone mobilePhone = new MobilePhone(); LandLinePhone landLinePhone = new LandLinePhone(); TestMarkerInterface.goTravel(mobilePhone); TestMarkerInterface.goTravel(landLinePhone); } public static void goTravel(Object o) throws NotMovableException { if (!(o instanceof Movable)) { System.out.println("you cannot use :" + o.getClass().getName() + " while travelling"); throw new NotMovableException(); } System.out.println("you can use :" + o.getClass().getName() + " while travelling"); }}
Sekarang saat kita menjalankan kelas utama:
you can use :com.MobilePhone while travelling you cannot use :com.LandLinePhone while travelling Exception in thread "main" com.NotMovableException: this object is not movable at com.TestMarkerInterface.goTravel(TestMarkerInterface.java:22) at com.TestMarkerInterface.main(TestMarkerInterface.java:14)
Jadi kelas mana pun yang mengimplementasikan antarmuka penanda
Movable
akan lulus ujian, jika tidak pesan kesalahan akan ditampilkan.Ini adalah cara
instanceOf
pemeriksaan operator dilakukan untuk Serializable , Cloneable dllsumber
Hal di atas dimulai sebagai salinan posting blog tetapi telah diedit sedikit untuk tata bahasa.
sumber
Antarmuka penanda seperti namanya hanya ada untuk memberi tahu apa pun yang mengetahuinya bahwa kelas mendeklarasikan sesuatu. Apa pun bisa berupa kelas JDK untuk
Serializable
antarmuka, atau kelas apa pun yang Anda tulis sendiri untuk yang khusus.b / Jika itu adalah antarmuka penanda, itu tidak boleh menyiratkan keberadaan metode apa pun - akan lebih baik untuk menyertakan metode yang tersirat dalam antarmuka. Tetapi Anda dapat memutuskan untuk mendesainnya sesuai keinginan jika Anda tahu mengapa Anda membutuhkannya
c / Ada sedikit perbedaan antara antarmuka kosong dan anotasi yang tidak menggunakan nilai atau parameter. Tetapi perbedaannya ada: anotasi dapat mendeklarasikan daftar kunci / nilai yang akan dapat diakses pada waktu proses.
sumber
Ini tidak ada hubungannya (harus) dengan JVM dan kompiler, itu ada hubungannya dengan kode apa pun yang tertarik dan sedang menguji antarmuka penanda yang diberikan.
Ini adalah keputusan desain dan dilakukan untuk alasan yang bagus. Lihat jawaban dari Audrius Meškauskas.
Sehubungan dengan topik khusus ini, menurut saya ini bukan masalah menjadi lebih baik atau lebih buruk. Antarmuka penanda melakukan apa yang seharusnya dilakukan dengan baik.
sumber
Sebuah. Saya selalu melihatnya sebagai pola desain dan tidak ada JVM-Khusus. Saya telah menggunakan pola itu dalam beberapa situasi.
c. Saya percaya bahwa menggunakan Anotasi untuk menandai sesuatu adalah solusi yang lebih baik daripada menggunakan antarmuka penanda. Hanya karena Antarmuka di tempat pertama ditujukan untuk mendefinisikan antarmuka umum Jenis / Kelas. Mereka adalah bagian dari hierarki kelas.
Anotasi ditujukan untuk memberikan Informasi Meta pada Kode, dan menurut saya penanda itu adalah informasi meta. Jadi mereka tepat untuk kasus penggunaan itu.
sumber
Tujuan utama antarmuka penanda adalah untuk membuat tipe khusus di mana tipe itu sendiri tidak memiliki perilaku sendiri.
public interface MarkerEntity { } public boolean save(Object object) throws InvalidEntityFoundException { if(!(object instanceof MarkerEntity)) { throw new InvalidEntityFoundException("Invalid Entity Found, can't be saved); } return db.save(object); }
Di sini menyimpan metode memastikan bahwa hanya objek kelas yang mengimplementasikan antarmuka MarkerEntity yang disimpan, untuk jenis lain InvalidEntityFoundException dilempar. Jadi di sini antarmuka marker MarkerEntity mendefinisikan tipe yang menambahkan perilaku khusus ke kelas yang mengimplementasikannya.
Meskipun anotasi juga dapat digunakan sekarang untuk menandai kelas untuk beberapa perlakuan khusus, tetapi anotasi marker adalah pengganti pola penamaan, bukan untuk antarmuka Marker.
Namun anotasi marker tidak dapat sepenuhnya menggantikan antarmuka marker karena; antarmuka marker digunakan untuk mendefinisikan tipe (seperti yang telah dijelaskan di atas) sedangkan anotasi marker tidak.
Sumber untuk komentar antarmuka penanda
sumber
Saya berpendapat pertama bahwa Serializable dan Cloneable adalah contoh buruk dari antarmuka penanda. Tentu, mereka berinteraksi dengan metode, tetapi menyiratkan metode, seperti
writeObject(ObjectOutputStream)
. (Kompilator akan membuatwriteObject(ObjectOutputStream)
metode untuk Anda jika Anda tidak menimpanya, dan semua objek telah memilikinyaclone()
, tetapi kompilator akan kembali membuatclone()
metode nyata untuk Anda tetapi dengan peringatan. Keduanya adalah kasus tepi aneh yang sebenarnya tidak contoh desain yang bagus.)Antarmuka penanda umumnya digunakan untuk salah satu dari dua tujuan:
1) Sebagai jalan pintas untuk menghindari tipe yang terlalu panjang, yang dapat terjadi dengan banyak obat generik. Misalnya, Anda memiliki tanda tangan metode ini:
public void doSomething(Foobar<String, Map<String, SomethingElse<Integer, Long>>>) { ... }
Itu berantakan dan menjengkelkan untuk mengetik, dan yang lebih penting, sulit dimengerti. Pertimbangkan ini sebagai gantinya:
public interface Widget extends Foobar<String, Map<String, SomethingElse<Integer, Long>>> { }
Kemudian metode Anda terlihat seperti ini:
public void doSomething(Widget widget) { ... }
Tidak hanya lebih jelas, tetapi Anda sekarang dapat Javadoc antarmuka Widget, dan juga lebih mudah untuk mencari semua kejadian dalam kode Widget Anda.
2) Antarmuka penanda juga dapat digunakan sebagai jalan untuk mengatasi kurangnya tipe persimpangan di Java. Dengan antarmuka penanda, Anda dapat meminta sesuatu dari dua tipe berbeda, seperti dalam tanda tangan metode. Katakanlah Anda memiliki beberapa Widget antarmuka dalam aplikasi Anda, seperti yang kami jelaskan di atas. Jika Anda memiliki metode yang memerlukan Widget yang juga memungkinkan Anda mengulanginya (ini dibuat-buat, tetapi bekerja dengan saya di sini), satu-satunya solusi yang baik adalah membuat antarmuka penanda yang memperluas kedua antarmuka:
public interface IterableWidget extends Iterable<String>, Widget { }
Dan di kode Anda:
public void doSomething(IterableWidget widget) { for (String s : widget) { ... } }
sumber
Serializable
atauCloneable
. Anda dapat memverifikasinya dengan memeriksa file kelas Anda. Lebih lanjut, membuat "antarmuka pintasan" bukanlah tentang antarmuka penanda. Dan ini adalah praktik pengkodean yang sangat buruk karena akan membutuhkan pembuatan kelas implementasi tambahan untuk memenuhi antarmuka tambahan. Omong-omong, Java memiliki tipe persimpangan selama satu dekade sekarang. Pelajari tentang Generik…Jika sebuah antarmuka tidak berisi metode apa pun dan dengan mengimplementasikan antarmuka itu, jika objek kita akan mendapatkan beberapa kemampuan, jenis antarmuka tersebut disebut antarmuka penanda.
sumber