Antarmuka Marker di Java?


136

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 Serializableantarmuka metode writeObject(Object)dari ObjectOutputStreammelakukan sesuatu seperti instanceOf Serializableuntuk mendeteksi apakah mengimplementasikan kelas Serializable& melempar NotSerializableExceptionsesuai. Semuanya ditangani dalam kode dan ini tampaknya menjadi pola desain jadi saya pikir kita bisa mendefinisikan antarmuka penanda kita sendiri.

Sekarang keraguan saya:

  1. Apakah definisi antarmuka marker yang disebutkan di atas pada poin pertama salah? Bagaimana kita bisa mendefinisikan antarmuka Marker?

  2. Dan alih-alih menggunakan instanceOfoperator, mengapa metodenya tidak bisa seperti itu writeObject(Serializable)sehingga ada pemeriksaan jenis waktu kompilasi daripada runtime?

  3. Bagaimana Anotasi lebih baik daripada Antarmuka Penanda?


8
Serializablekarena Anotasi tidak masuk akal dan @NonNullsebagai Antarmuka tidak masuk akal. Saya akan mengatakan: Anotasi adalah Penanda + Metadata. BTW: Pelopor Anotasi adalah XDoclet, lahir di Javadoc, dibunuh oleh Anotasi.
Suram

Jawaban:


119
  1. Apakah definisi antarmuka marker yang disebutkan di atas pada poin pertama salah? - Memang benar di bagian (1) antarmuka marker harus kosong, dan (2) mengimplementasikannya dimaksudkan untuk menyiratkan beberapa perlakuan khusus dari kelas pelaksana. Bagian yang salah adalah menyatakan bahwa JVM atau kompilator akan memperlakukan objek kelas itu secara berbeda: Anda benar dalam mengamati bahwa itu adalah kode perpustakaan kelas Java yang memperlakukan objek ini sebagai dapat dikloning, dapat diserialkan, dll. tidak ada hubungannya dengan kompiler atau JVM.
  2. Alih-alih menggunakan operator instanceOf mengapa metode tidak bisa seperti itu writeObject(Serializable)sehingga ada pemeriksaan jenis waktu kompilasi - Ini memungkinkan Anda menghindari pencemaran kode Anda dengan nama antarmuka penanda saat "biasa Object" diperlukan. Misalnya, jika Anda membuat kelas yang perlu dibuat bersambung, dan memiliki anggota objek, Anda akan dipaksa untuk melakukan transmisi atau membuat objek Serializablepada waktu kompilasi. Ini tidak nyaman, karena antarmukanya tidak memiliki fungsi apa pun.
  3. Bagaimana Anotasi lebih baik daripada Antarmuka Penanda? - Mereka memungkinkan Anda mencapai tujuan yang sama dalam menyampaikan metadata tentang kelas kepada konsumennya tanpa membuat tipe terpisah untuknya. Anotasi juga lebih efektif, memungkinkan pemrogram meneruskan informasi yang lebih canggih ke kelas yang "mengonsumsinya".

14
Cara saya selalu memahaminya adalah bahwa Anotasi adalah semacam 'Antarmuka Penanda 2.0': Dapat diserialkan ada sejak Java 1.1, Anotasi ditambahkan dalam 1.5
blagae

dan karena writeObjectbersifat pribadi, itu berarti bahwa misalnya kelas tidak harus memanggil implementasi superclass
ratchet freak

15
Satu hal yang perlu diingat adalah bahwa penjelasan ditambahkan ke bahasa beberapa tahun setelah perpustakaan standar awalnya dirancang. Jika anotasi menggunakan bahasa tersebut sejak awal, diragukan bahwa Serializable akan menjadi antarmuka, mungkin itu adalah anotasi.
Theodore Norvell

Nah, dalam kasus Cloneableini tidak jelas apakah itu perpustakaan atau perlakuan JVM dari instance yang diubah.
Holger

"[...] tanpa membuat tipe terpisah untuk itu." - Saya akan mengatakan bahwa inilah yang membedakan mereka: Antarmuka marker memang memperkenalkan suatu tipe, sedangkan anotasi (jenis) tidak.
aioobe

22

Hal ini tidak mungkin untuk menegakkan Serializableatas writeObjectkarena 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 (seperti Object) tidak berarti bahwa contoh yang dirujuk tidak dapat benar-benar berseri. Misalnya dalam

   Object x = "abc";
   if (x instanceof Serializable) {
   }

kelas induk ( Object) tidak dapat diserialisasi dan akan dijalankan menggunakan konstruktor tanpa parameternya. Nilai yang direferensikan oleh x,, Stringdapat bersambung dan pernyataan bersyarat akan dijalankan.


7

Saya telah membuat demonstrasi sederhana untuk menyelesaikan keraguan no 1 dan 2:

Kami akan memiliki antarmuka Movable yang akan diimplementasikan oleh MobilePhone.javaKelas dan satu kelas lagi LandlinePhone.javayang TIDAK menerapkan antarmuka Movable

Antarmuka penanda kami:

package com;

public interface Movable {

}

LandLinePhone.java dan MobilePhone.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 Movableakan lulus ujian, jika tidak pesan kesalahan akan ditampilkan.

Ini adalah cara instanceOfpemeriksaan operator dilakukan untuk Serializable , Cloneable dll


6

Antarmuka marker di Java adalah antarmuka tanpa kolom atau metode. Sederhananya, antarmuka kosong di Java disebut antarmuka penanda. Contoh antarmuka penanda adalah Serializable, Cloneabledan Remoteantarmuka. Ini digunakan untuk menunjukkan beberapa informasi ke kompiler atau JVM. Jadi jika JVM melihat bahwa sebuah kelas adalah Serializable, ia dapat melakukan beberapa operasi khusus padanya. Demikian pula, jika JVM melihat beberapa kelas sedang diimplementasikan Cloneable, JVM dapat melakukan beberapa operasi untuk mendukung kloning. Hal yang sama berlaku untuk RMI dan Remoteantarmuka. Jadi singkatnya, antarmuka penanda menunjukkan sinyal atau perintah ke kompiler atau JVM.

Hal di atas dimulai sebagai salinan posting blog tetapi telah diedit sedikit untuk tata bahasa.


6
Tidak apa-apa untuk menyalin tetapi sebutkan juga sumbernya: javarevisited.blogspot.com/2012/01/… . Juga akan menyenangkan jika Anda tidak menyalin dan menempelkan kesalahan ejaan juga. :)
Saurabh Patil

5

Antarmuka penanda seperti namanya hanya ada untuk memberi tahu apa pun yang mengetahuinya bahwa kelas mendeklarasikan sesuatu. Apa pun bisa berupa kelas JDK untuk Serializableantarmuka, 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.


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

  2. Ini adalah keputusan desain dan dilakukan untuk alasan yang bagus. Lihat jawaban dari Audrius Meškauskas.

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


Bisakah Anda menambahkan link ke "jawaban dari Audrius Meškauskas"? Saya tidak melihat apa pun di halaman ini dengan nama itu.
Sarah Messer

3

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.


2

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


1
+1 untuk menunjukkan bahwa itu sebenarnya digunakan sebagai pengait "pengaman" belaka yang harus dikatakan oleh programmer secara eksplisit bahwa itu dapat disimpan untuk contoh database.
Ludvig W

1

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 membuat writeObject(ObjectOutputStream)metode untuk Anda jika Anda tidak menimpanya, dan semua objek telah memilikinya clone(), tetapi kompilator akan kembali membuat clone()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) { ... }
}

1
Kompilator tidak membuat metode apa pun jika kelas Anda mengimplementasikan Serializableatau Cloneable. 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…
Holger

Untuk dapat dikloning, Anda benar. Itu mengubah apa yang dilakukan Object.clone (). Itu masih mengerikan. Lihat tautan Josh Bloch Antarmuka pintasan belum tentu merupakan pola desain yang baik, karena Anda secara sewenang-wenang membatasi apa yang dapat Anda kirim ke suatu metode. Pada saat yang sama, saya merasa bahwa menulis kode yang lebih jelas, jika sedikit lebih membatasi, biasanya merupakan pertukaran yang masuk akal. Adapun jenis persimpangan, itu hanya berlaku untuk obat generik, dan tidak dapat didenotasikan, dan karenanya tidak berguna dalam banyak kesempatan. Cobalah memiliki variabel instan yang dapat berseri dan, yah, yang lainnya.
MikeyB

0

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.

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.