Apa itu refleksi dan mengapa itu berguna?

2124

Apa itu refleksi, dan mengapa itu berguna?

Saya khususnya tertarik pada Java, tetapi saya berasumsi prinsipnya sama dalam bahasa apa pun.

Lehane
sumber
9
Bagi saya itu adalah cara untuk mendapatkan nama kelas saat runtime dan membuat objek dari kelas itu.
Nabin
65
karena ini adalah pertanyaan populer yang ingin saya tunjukkan bahwa perenungan (Tanpa anotasi) harus menjadi alat terakhir yang Anda gunakan ketika menyelesaikan masalah. Saya menggunakannya dan menyukainya, tetapi membatalkan semua keuntungan dari pengetikan statis Java. Jika Anda benar-benar membutuhkannya, pisahkan dengan area sekecil mungkin (Satu metode atau satu kelas). Ini lebih dapat diterima untuk menggunakannya dalam pengujian daripada kode produksi. Dengan anotasi itu harus baik-baik saja - Poin utamanya bukan untuk menentukan nama kelas atau metode sebagai "Strings" jika Anda dapat menghindarinya.
Bill K
4
Selain komentar @ BillK: Refleksi sangat kuat, saya akan menyebutnya ajaib. Dengan kekuatan besar datang tanggung jawab besar. Gunakan hanya jika Anda tahu apa yang Anda lakukan.
MC Emperor
Anda dapat menghindari banyak perangkap yang terlibat dengan refleksi menggunakan Manifold @Jailbreak. Ini memberikan akses langsung, tipe-aman ke bidang pribadi, metode, dll. Biarkan kompiler Java dengan aman memverifikasi kode Anda dan biarkan Manifold menghasilkan akses refleksi yang mendasari untuk Anda. Pelajari lebih lanjut: manifold.systems/docs.html#type-safe-reflection
Scott

Jawaban:

1716

Refleksi nama digunakan untuk menggambarkan kode yang dapat memeriksa kode lain dalam sistem yang sama (atau dirinya sendiri).

Misalnya, Anda memiliki objek dengan tipe yang tidak dikenal di Java, dan Anda ingin memanggil metode 'doSomething' jika ada. Sistem pengetikan statis Java tidak benar-benar dirancang untuk mendukung ini kecuali objeknya sesuai dengan antarmuka yang dikenal, tetapi menggunakan refleksi, kode Anda dapat melihat objek dan mencari tahu apakah ia memiliki metode yang disebut 'doSomething' dan kemudian memanggilnya jika Anda ingin.

Jadi, untuk memberi Anda contoh kode ini di Jawa (bayangkan objek yang dimaksud adalah foo):

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

Satu kasus penggunaan yang sangat umum di Jawa adalah penggunaan dengan anotasi. JUnit 4, misalnya, akan menggunakan refleksi untuk melihat kelas Anda untuk metode yang ditandai dengan penjelasan @Test, dan kemudian akan memanggil mereka ketika menjalankan unit test.

Ada beberapa contoh refleksi yang baik untuk memulai di http://docs.oracle.com/javase/tutorial/reflect/index.html

Dan akhirnya, ya, konsepnya cukup mirip dengan bahasa yang diketik secara statis lainnya yang mendukung refleksi (seperti C #). Dalam bahasa yang diketik secara dinamis, use case yang dijelaskan di atas kurang perlu (karena kompiler akan memungkinkan metode apa pun dipanggil pada objek apa pun, gagal saat runtime jika tidak ada), tetapi kasus kedua mencari metode yang ditandai atau bekerja dengan cara tertentu masih umum.

Perbarui dari komentar:

Kemampuan untuk memeriksa kode dalam sistem dan melihat jenis objek bukan refleksi, melainkan Tipe Introspeksi. Refleksi adalah kemampuan untuk membuat modifikasi saat runtime dengan memanfaatkan introspeksi. Perbedaan ini diperlukan di sini karena beberapa bahasa mendukung introspeksi, tetapi tidak mendukung refleksi. Salah satu contohnya adalah C ++

Matt Sheppard
sumber
32
dapatkah Anda menjelaskan apa pentingnya parameter nol di baris ini Metode metode = foo.getClass (). getMethod ("doSomething", null);
Krsna Chaitanya
54
Nol menunjukkan tidak ada parameter yang diteruskan ke metode foo. Lihat docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...) untuk detailnya.
Matt Sheppard
791
Hanya untuk menjernihkan karena ini memiliki begitu banyak upvotes. Kemampuan untuk memeriksa kode dalam sistem dan melihat jenis objek bukan refleksi, melainkan Tipe Introspeksi. Refleksi adalah kemampuan untuk membuat modifikasi saat runtime dengan memanfaatkan introspeksi. Perbedaan ini diperlukan di sini karena beberapa bahasa mendukung introspeksi, tetapi tidak mendukung refleksi. Salah satu contohnya adalah C ++.
bigtunacan
39
Saya suka refleksi tetapi jika Anda memiliki kontrol atas kode kemudian menggunakan refleksi seperti yang ditentukan dalam jawaban ini tidak sehat dan karenanya merupakan penyalahgunaan - Anda harus menggunakan Introspeksi Jenis (instanceof) dan tipe yang kuat. Jika ada cara selain refleksi untuk melakukan sesuatu, itulah yang harus dilakukan. Refleksi menyebabkan sakit hati yang serius karena Anda kehilangan semua keuntungan menggunakan bahasa yang diketik secara statis. Jika Anda membutuhkannya, Anda memerlukannya, namun demikian saya akan mempertimbangkan solusi pra-paket seperti Spring atau sesuatu yang sepenuhnya merangkum refleksi yang diperlukan - IE: biarkan orang lain sakit kepala.
Bill K
6
@ bigtunacan Dari mana Anda mendapatkan informasi itu? Saya melihat istilah "refleksi" yang digunakan dalam dokumentasi Java resmi dari Oracle untuk menjelaskan tidak hanya kemampuan untuk membuat perubahan saat runtime tetapi juga kemampuan untuk melihat jenis objek. Belum lagi bahwa yang disebut paling "jenis introspeksi" kelas terkait (ex: Method, Constructor, Modifier, Field, Member, pada dasarnya tampaknya semua kecuali Class) berada dalam java.lang.*reflect*paket. Mungkin konsep "refleksi" secara komprehensif mencakup "tipe introspeksi" dan modifikasi pada saat run-time?
RestInPeace
246

Refleksi adalah kemampuan bahasa untuk memeriksa dan secara dinamis memanggil kelas, metode, atribut, dll. Pada saat runtime.

Misalnya, semua objek di Jawa memiliki metode getClass(), yang memungkinkan Anda menentukan kelas objek bahkan jika Anda tidak mengetahuinya pada waktu kompilasi (misalnya jika Anda menyatakannya sebagai Object) - ini mungkin tampak sepele, tetapi refleksi seperti itu tidak mungkin dalam bahasa yang kurang dinamis seperti C++. Penggunaan yang lebih maju memungkinkan Anda membuat daftar dan memanggil metode, konstruktor, dll.

Refleksi penting karena memungkinkan Anda menulis program yang tidak harus "mengetahui" semuanya pada waktu kompilasi, membuatnya lebih dinamis, karena mereka dapat diikat bersama saat runtime. Kode dapat ditulis terhadap antarmuka yang dikenal, tetapi kelas aktual yang akan digunakan dapat dipakai menggunakan refleksi dari file konfigurasi.

Banyak kerangka kerja modern menggunakan refleksi secara luas karena alasan ini. Sebagian besar bahasa modern lainnya menggunakan refleksi juga, dan dalam bahasa scripting (seperti Python) mereka bahkan lebih terintegrasi, karena terasa lebih alami dalam model pemrograman umum bahasa-bahasa tersebut.

Liedman
sumber
2
Jadi dengan kata lain, Anda dapat membuat instance dari nama yang memenuhi syarat dan kompiler tidak akan mengeluh tentang hal itu (karena katakan Anda menggunakan hanya String untuk nama kelas). Kemudian, pada saat run time, jika kelas itu tidak ada, Anda mendapatkan pengecualian. Anda semacam melewati kompiler dalam hal ini. Apakah Anda memberi saya beberapa use case khusus untuk ini? Saya tidak bisa membayangkan kapan saya akan memilihnya.
Fernando Gabrieli
3
@FernandoGabrieli sementara memang benar bahwa mudah untuk membuat kesalahan runtime dengan refleksi, juga sangat mungkin untuk menggunakan refleksi tanpa mempertaruhkan pengecualian runtime. Seperti yang ditunjukkan dalam jawaban saya, penggunaan refleksi yang umum untuk perpustakaan atau kerangka kerja, yang secara eksplisit tidak dapat mengetahui struktur aplikasi pada waktu kompilasi, karena mereka dikompilasi terpisah dari aplikasi. Setiap perpustakaan yang menggunakan "kode dengan konvensi" cenderung menggunakan refleksi, tetapi tidak harus menggunakan string ajaib.
Liedman
1
C++memang memiliki informasi jenis Run-time. RTTI
Ayxan
114

Salah satu kegunaan refleksi favorit saya adalah metode dump Java di bawah ini. Dibutuhkan objek apa pun sebagai parameter dan menggunakan API refleksi Java untuk mencetak setiap nama bidang dan nilai.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}
Ben Williams
sumber
8
Untuk apa Callcount diatur?
Tom
8
Saya mendapat pengecualian di utas "AWT-EventQueue-0" java.lang.StackOverflowError ketika saya menjalankan ini.
Tom
3
@ Tom callCountharus disetel ke nol. Nilainya digunakan untuk menentukan berapa banyak tab yang harus mendahului setiap baris output: setiap kali dump perlu membuang "subobject", output akan dicetak sebagai bersarang di induknya. Metode itu terbukti berguna ketika dibungkus dengan yang lain. Pertimbangkan printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny
1
Java.lang.StackOverflowError dapat dibuat jika ada referensi melingkar, karena rekursi yang tidak dicentang: buffer.append (dump (nilai, callCount))
Arnaud P
4
Bisakah Anda secara khusus merilis kode Anda ke Domain Publik, tolong?
stolsvik
84

Penggunaan Refleksi

Refleksi umumnya digunakan oleh program yang membutuhkan kemampuan untuk memeriksa atau memodifikasi perilaku runtime aplikasi yang berjalan di mesin virtual Java. Ini adalah fitur yang relatif canggih dan harus digunakan hanya oleh pengembang yang memiliki pemahaman yang kuat tentang dasar-dasar bahasa. Dengan peringatan itu dalam pikiran, refleksi adalah teknik yang kuat dan dapat memungkinkan aplikasi untuk melakukan operasi yang seharusnya tidak mungkin dilakukan.

Fitur Ekstensibilitas

Aplikasi dapat menggunakan kelas eksternal yang ditentukan pengguna dengan membuat instance objek yang dapat diperluas menggunakan nama yang sepenuhnya memenuhi syarat. Browser Kelas dan Lingkungan Pengembangan Visual Browser kelas harus dapat menyebutkan anggota kelas. Lingkungan pengembangan visual dapat mengambil manfaat dari penggunaan informasi jenis yang tersedia dalam refleksi untuk membantu pengembang dalam menulis kode yang benar. Debugger dan Alat Uji Debugger harus dapat memeriksa anggota pribadi di kelas. Rangkaian uji dapat menggunakan refleksi untuk secara sistematis memanggil set API yang dapat ditemukan yang ditentukan pada kelas, untuk memastikan tingkat tinggi cakupan kode dalam rangkaian uji.

Kerugian Refleksi

Refleksi itu kuat, tetapi tidak boleh digunakan tanpa pandang bulu. Jika dimungkinkan untuk melakukan operasi tanpa menggunakan refleksi, maka lebih baik untuk menghindari menggunakannya. Perhatian berikut harus diingat ketika mengakses kode melalui refleksi.

  • Overhead kinerja

Karena refleksi melibatkan jenis yang diselesaikan secara dinamis, optimasi mesin virtual Java tertentu tidak dapat dilakukan. Akibatnya, operasi reflektif memiliki kinerja lebih lambat daripada rekan non-reflektif mereka dan harus dihindari di bagian kode yang sering disebut dalam aplikasi yang sensitif terhadap kinerja.

  • Pembatasan Keamanan

Refleksi memerlukan izin runtime yang mungkin tidak ada saat berjalan di bawah manajer keamanan. Ini merupakan pertimbangan penting untuk kode yang harus dijalankan dalam konteks keamanan terbatas, seperti dalam Applet.

  • Paparan Internal

Karena refleksi memungkinkan kode untuk melakukan operasi yang ilegal dalam kode non-reflektif, seperti mengakses bidang dan metode pribadi, penggunaan refleksi dapat mengakibatkan efek samping yang tidak terduga, yang dapat membuat kode menjadi tidak berfungsi dan dapat merusak portabilitas. Kode reflektif memecah abstraksi dan karenanya dapat mengubah perilaku dengan peningkatan platform.

sumber: API Refleksi

Desmond Smith
sumber
44

Refleksi adalah mekanisme kunci untuk memungkinkan aplikasi atau kerangka kerja untuk bekerja dengan kode yang mungkin belum ditulis!

Ambil contoh file web.xml khas Anda. Ini akan berisi daftar elemen servlet, yang berisi elemen kelas servlet bersarang. Wadah servlet akan memproses file web.xml, dan membuat contoh baru baru dari setiap kelas servlet melalui refleksi.

Contoh lain adalah Java API untuk XML Parsing (JAXP) . Di mana penyedia parser XML 'terhubung' melalui properti sistem terkenal, yang digunakan untuk membuat instance baru melalui refleksi.

Dan akhirnya, contoh paling komprehensif adalah Spring yang menggunakan refleksi untuk membuat biji-bijiannya, dan untuk penggunaan proksi yang berat

toolkit
sumber
36

Tidak setiap bahasa mendukung refleksi tetapi prinsip-prinsipnya biasanya sama dalam bahasa yang mendukungnya.

Refleksi adalah kemampuan untuk "merefleksikan" pada struktur program Anda. Atau lebih konkret. Untuk melihat objek dan kelas yang Anda miliki dan secara terprogram mendapatkan kembali informasi tentang metode, bidang, dan antarmuka yang mereka implementasikan. Anda juga dapat melihat hal-hal seperti anotasi.

Ini berguna dalam banyak situasi. Di mana pun Anda ingin dapat secara dinamis memasukkan kelas ke dalam kode Anda. Banyak pembuat pemetaan relasional objek menggunakan refleksi untuk dapat membuat instance objek dari database tanpa mengetahui terlebih dahulu objek apa yang akan mereka gunakan. Arsitektur plug-in adalah tempat lain di mana refleksi berguna. Mampu memuat kode secara dinamis dan menentukan apakah ada jenis di sana yang mengimplementasikan antarmuka yang tepat untuk digunakan sebagai plugin adalah penting dalam situasi tersebut.

Mendelt
sumber
Saya perlu instantiate objek berdasarkan data yang ada di DB. Saya percaya ini yang Anda katakan. Kode contoh akan banyak membantu saya. Terima kasih sebelumnya.
Atom
34

Refleksi memungkinkan instantiasi objek baru, doa metode, dan mendapatkan / set operasi pada variabel kelas secara dinamis pada saat run time tanpa memiliki pengetahuan sebelumnya tentang implementasinya.

Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();

//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class); 

Object returnValue = method.invoke(null, "parameter-value1");

Dalam contoh di atas parameter nol adalah objek yang ingin Anda panggil metode ini. Jika metode ini statis, Anda memberikan nol. Jika metode ini tidak statis, maka sambil memohon Anda harus memberikan contoh MyObject yang valid, bukan nol

Refleksi juga memungkinkan Anda untuk mengakses anggota pribadi / metode kelas:

public class A{

  private String str= null;

  public A(String str) {
  this.str= str;
  }
}

.

A obj= new A("Some value");

Field privateStringField = A.class.getDeclaredField("privateString");

//Turn off access check for this field
privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
  • Untuk inspeksi kelas (juga dikenal sebagai introspeksi), Anda tidak perlu mengimpor paket refleksi ( java.lang.reflect). Metadata kelas dapat diakses melalui java.lang.Class.

Refleksi adalah API yang sangat kuat tetapi dapat memperlambat aplikasi jika digunakan secara berlebihan, karena ia menyelesaikan semua jenis saat runtime.

Nikhil Shekhar
sumber
21

Java Reflection cukup kuat dan bisa sangat berguna. Java Reflection memungkinkan untuk memeriksa kelas, antarmuka, bidang dan metode saat runtime, tanpa mengetahui nama-nama kelas, metode, dll. Pada waktu kompilasi. Dimungkinkan juga untuk instantiate objek baru, memanggil metode dan mendapatkan / set nilai bidang menggunakan refleksi.

Contoh Java Reflection yang cepat untuk menunjukkan kepada Anda seperti apa bentuk refleksi itu:

Method[] methods = MyObject.class.getMethods();

    for(Method method : methods){
        System.out.println("method = " + method.getName());
    }

Contoh ini memperoleh objek Kelas dari kelas yang disebut MyObject. Menggunakan objek kelas contoh mendapat daftar metode di kelas itu, iterates metode dan cetak nama mereka.

Bagaimana cara kerjanya dijelaskan di sini

Sunting : Setelah hampir 1 tahun saya mengedit jawaban ini karena ketika membaca tentang refleksi saya mendapat beberapa kegunaan Refleksi lagi.

  • Spring menggunakan konfigurasi bean seperti:


<bean id="someID" class="com.example.Foo">
    <property name="someField" value="someValue" />
</bean>

Ketika konteks Spring memproses elemen <bean> ini, ia akan menggunakan Class.forName (String) dengan argumen "com.example.Foo" untuk membuat instance Kelas itu.

Kemudian akan menggunakan refleksi lagi untuk mendapatkan setter yang sesuai untuk elemen <properti> dan mengatur nilainya ke nilai yang ditentukan.

  • Junit menggunakan Refleksi terutama untuk menguji metode Pribadi / Terlindungi.

Untuk metode pribadi,

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Untuk bidang pribadi,

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
VdeX
sumber
21

Contoh:

Ambil contoh aplikasi jarak jauh yang memberi aplikasi Anda objek yang Anda peroleh menggunakan Metode API mereka. Sekarang berdasarkan pada objek Anda mungkin perlu melakukan semacam perhitungan.

Penyedia menjamin bahwa objek dapat dari 3 jenis dan kita perlu melakukan perhitungan berdasarkan pada jenis objek apa.

Jadi kita dapat menerapkan dalam 3 kelas masing-masing berisi logika yang berbeda. Jelas informasi objek tersedia dalam runtime sehingga Anda tidak dapat secara statis kode untuk melakukan perhitungan maka refleksi digunakan untuk instantiate objek kelas yang Anda perlukan untuk melakukan perhitungan berdasarkan pada objek yang diterima dari penyedia.

manusia.js
sumber
Saya memerlukan sesuatu yang serupa .. Contoh akan banyak membantu saya karena saya baru mengenal konsep refleksi ..
Atom
2
Saya bingung: tidak bisakah Anda menggunakan instanceofuntuk menentukan tipe objek saat runtime?
ndm13
19

Contoh sederhana untuk refleksi. Dalam permainan catur, Anda tidak tahu apa yang akan dipindahkan oleh pengguna pada saat run time. refleksi dapat digunakan untuk memanggil metode yang sudah diterapkan pada saat run time:

public class Test {

    public void firstMoveChoice(){
        System.out.println("First Move");
    } 
    public void secondMOveChoice(){
        System.out.println("Second Move");
    }
    public void thirdMoveChoice(){
        System.out.println("Third Move");
    }

    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
        Test test = new Test();
        Method[] method = test.getClass().getMethods();
        //firstMoveChoice
        method[0].invoke(test, null);
        //secondMoveChoice
        method[1].invoke(test, null);
        //thirdMoveChoice
        method[2].invoke(test, null);
    }

}
Isuru Jayakantha
sumber
18

Refleksi adalah API yang digunakan untuk memeriksa atau memodifikasi perilaku metode, kelas, antarmuka saat runtime.

  1. Kelas-kelas yang diperlukan untuk refleksi disediakan di bawah java.lang.reflect package.
  2. Refleksi memberi kita informasi tentang kelas yang menjadi objek dan juga metode kelas yang dapat dieksekusi dengan menggunakan objek.
  3. Melalui refleksi kita dapat memanggil metode pada saat runtime terlepas dari specifier akses yang digunakan dengannya.

Paket java.langdan java.lang.reflectmenyediakan kelas untuk refleksi java.

Refleksi dapat digunakan untuk mendapatkan informasi tentang -

  1. Kelas The getClass()metode yang digunakan untuk mendapatkan nama dari kelas yang objek milik.

  2. Konstruktor The getConstructors()metode yang digunakan untuk mendapatkan konstruktor publik dari kelas mana objek milik.

  3. Metode tersebut getMethods()metode yang digunakan untuk mendapatkan metode umum dari kelas mana suatu benda milik.

The API Refleksi terutama digunakan dalam:

IDE (Lingkungan Pengembangan Terpadu) misalnya Eclipse, MyEclipse, NetBeans dll.
Debugger dan Alat Uji dll.

Keuntungan Menggunakan Refleksi:

Fitur Ekstensibilitas: Aplikasi dapat menggunakan kelas eksternal yang ditentukan pengguna dengan membuat turunan objek ekstensi menggunakan nama mereka yang sepenuhnya memenuhi syarat.

Alat debugging dan pengujian: Debugger menggunakan properti refleksi untuk memeriksa anggota pribadi di kelas.

Kekurangan:

Overhead Kinerja: Operasi reflektif memiliki kinerja lebih lambat daripada rekan non-reflektifnya, dan harus dihindari di bagian kode yang sering disebut dalam aplikasi yang sensitif terhadap kinerja.

Paparan Internal: Kode reflektif memecah abstraksi dan karena itu dapat mengubah perilaku dengan peningkatan platform.

Ref: Java Reflection javarevisited.blogspot.in

roottraveller
sumber
4
Saya akan menambah kekurangannya " Ini merusak refactoring ". Bagi saya itulah alasan utama untuk menghindari refleksi sebanyak mungkin.
SantiBailors
Jadi itu memungkinkan kita (misalnya), untuk memeriksa kelas yang kita miliki (apakah kita memiliki instance atau tidak), benar? Maksud saya, dapatkan metode atau konstruktor mereka dan menggunakannya untuk membuat instance baru / memanggil mereka. Mengapa kita mengatakan "mengubah perilaku program" jika perilaku sudah ada tetapi dengan kode yang berbeda? Mengapa itu disebut "refleksi"? Terima kasih
Fernando Gabrieli
15

Sesuai pemahaman saya:

Refleksi memungkinkan pemrogram untuk mengakses entitas dalam program secara dinamis. yaitu ketika sedang mengkode aplikasi jika programmer tidak mengetahui tentang suatu kelas atau metodenya, ia dapat menggunakan kelas tersebut secara dinamis (pada saat run time) dengan menggunakan refleksi.

Ini sering digunakan dalam skenario di mana nama kelas sering berubah. Jika situasi seperti itu muncul, maka rumit bagi programmer untuk menulis ulang aplikasi dan mengubah nama kelas berulang kali.

Sebaliknya, dengan menggunakan refleksi, perlu khawatir tentang kemungkinan perubahan nama kelas.

pramod
sumber
15

Refleksi adalah serangkaian fungsi yang memungkinkan Anda untuk mengakses informasi runtime dari program Anda dan memodifikasinya perilaku (dengan beberapa batasan).

Ini berguna karena memungkinkan Anda untuk mengubah perilaku runtime tergantung pada informasi meta program Anda, yaitu, Anda dapat memeriksa jenis pengembalian fungsi dan mengubah cara Anda menangani situasi.

Dalam C # misalnya Anda dapat memuat perakitan (.dll) dalam runtime memeriksanya, menavigasi kelas dan mengambil tindakan sesuai dengan apa yang Anda temukan. Itu juga memungkinkan Anda membuat turunan kelas saat runtime, menjalankan metode, dll.

Di mana itu bisa berguna? Tidak berguna setiap saat tetapi untuk situasi konkret. Misalnya, Anda dapat menggunakannya untuk mendapatkan nama kelas untuk tujuan logging, untuk secara dinamis membuat handler untuk acara sesuai dengan apa yang ditentukan pada file konfigurasi dan seterusnya ...

Jorge Córdoba
sumber
11

Saya hanya ingin menambahkan beberapa poin ke semua yang terdaftar.

Dengan API Refleksi Anda dapat menulis toString()metode universal untuk objek apa pun.

Ini berguna saat debugging.

Berikut ini beberapa contohnya:

class ObjectAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * Converts an object to a string representation that lists all fields.
    * @param obj an object
    * @return a string with the object's class name and all field names and
    * values
    */
   public String toString(Object obj) {
      if (obj == null) return "null";
      if (visited.contains(obj)) return "...";
      visited.add(obj);
      Class cl = obj.getClass();
      if (cl == String.class) return (String) obj;
      if (cl.isArray()) {
         String r = cl.getComponentType() + "[]{";
         for (int i = 0; i < Array.getLength(obj); i++) {
            if (i > 0) r += ",";
            Object val = Array.get(obj, i);
            if (cl.getComponentType().isPrimitive()) r += val;
            else r += toString(val);
         }
         return r + "}";
      }

      String r = cl.getName();
      // inspect the fields of this class and all superclasses
      do {
         r += "[";
         Field[] fields = cl.getDeclaredFields();
         AccessibleObject.setAccessible(fields, true);
         // get the names and values of all fields
         for (Field f : fields) {
            if (!Modifier.isStatic(f.getModifiers())) {
               if (!r.endsWith("[")) r += ",";
               r += f.getName() + "=";
               try {
                  Class t = f.getType();
                  Object val = f.get(obj);
                  if (t.isPrimitive()) r += val;
                  else r += toString(val);
               } catch (Exception e) {
                  e.printStackTrace();
               }
            }
         }
         r += "]";
         cl = cl.getSuperclass();
      } while (cl != null);

      return r;
   }    
}
nazar_art
sumber
11

Refleksi adalah membiarkan objek melihat penampilan mereka. Argumen ini sepertinya tidak ada hubungannya dengan refleksi. Sebenarnya, ini adalah kemampuan "mengidentifikasi diri".

Refleksi itu sendiri adalah kata untuk bahasa-bahasa seperti itu yang tidak memiliki kemampuan pengetahuan-diri dan penginderaan-diri seperti Java dan C #. Karena mereka tidak memiliki kemampuan pengetahuan-diri, ketika kita ingin mengamati bagaimana kelihatannya, kita harus memiliki hal lain untuk merenungkan bagaimana itu terlihat. Bahasa dinamis yang sangat baik seperti Ruby dan Python dapat merasakan pantulan mereka sendiri tanpa bantuan orang lain. Kita dapat mengatakan bahwa objek Jawa tidak dapat melihat bagaimana tampilannya tanpa cermin, yang merupakan objek dari kelas refleksi, tetapi objek dengan Python dapat melihatnya tanpa cermin. Jadi itu sebabnya kita perlu refleksi di Jawa.

Marcus Thornton
sumber
ketik (), isinstance (), callable (), dir () dan getattr (). .... ini adalah panggilan pantulan pythonic
AnthonyJClink
9

Dari halaman dokumentasi java

java.lang.reflectpaket menyediakan kelas dan antarmuka untuk mendapatkan informasi reflektif tentang kelas dan objek. Refleksi memungkinkan akses programatik ke informasi tentang bidang, metode dan konstruktor dari kelas yang dimuat, dan penggunaan bidang, metode, dan konstruktor yang direfleksikan untuk beroperasi pada rekan-rekan mereka yang mendasarinya, dalam batasan keamanan.

AccessibleObjectmemungkinkan penindasan pemeriksaan akses jika diperlukan ReflectPermission.

Kelas-kelas dalam paket ini, bersama dengan java.lang.Classmengakomodasi aplikasi seperti debugger, juru bahasa, pemeriksa objek, peramban kelas, dan layanan seperti Object Serializationdan JavaBeansyang memerlukan akses ke anggota publik dari objek target (berdasarkan kelas runtime-nya) atau anggota yang dideklarasikan oleh kelas yang diberikan

Ini mencakup fungsionalitas berikut.

  1. Memperoleh objek Kelas,
  2. Meneliti properti suatu kelas (bidang, metode, konstruktor),
  3. Menetapkan dan mendapatkan nilai bidang,
  4. Metode memohon,
  5. Membuat instance objek baru.

Lihatlah tautan dokumentasi ini untuk metode yang diekspos oleh Classkelas.

Dari artikel ini (oleh Dennis Sosnoski, President, Sosnoski Software Solutions, Inc) dan artikel ini (security-explorations pdf):

Saya bisa melihat banyak kekurangan daripada penggunaan menggunakan Refleksi

Pengguna Refleksi:

  1. Ini memberikan cara serbaguna yang secara dinamis menghubungkan komponen program
  2. Ini berguna untuk membuat perpustakaan yang bekerja dengan objek dengan cara yang sangat umum

Kerugian Refleksi:

  1. Refleksi jauh lebih lambat daripada kode langsung ketika digunakan untuk akses bidang dan metode.
  2. Itu dapat mengaburkan apa yang sebenarnya terjadi di dalam kode Anda
  3. Itu memotong kode sumber dapat membuat masalah pemeliharaan
  4. Kode refleksi juga lebih kompleks daripada kode langsung yang sesuai
  5. Ini memungkinkan pelanggaran batasan keamanan utama Java seperti perlindungan akses data dan keamanan tipe

Pelanggaran umum:

  1. Memuat kelas terbatas,
  2. Memperoleh referensi untuk konstruktor, metode atau bidang kelas terbatas,
  3. Pembuatan instance objek baru, pemanggilan metode, mendapatkan atau menyetel nilai bidang dari kelas terbatas.

Lihatlah pertanyaan SE ini mengenai penyalahgunaan fitur refleksi:

Bagaimana cara membaca bidang pribadi di Jawa?

Ringkasan:

Penggunaan fungsi yang tidak aman yang dilakukan dari dalam kode sistem juga dapat dengan mudah menyebabkan kompromi mode keamanan Java l. Jadi gunakan fitur ini dengan hemat

Ravindra babu
sumber
Cara untuk menghindari masalah kinerja Reflection dalam beberapa kasus adalah dengan meminta kelas Woozlememeriksa kelas lain pada startup untuk melihat mana yang memiliki RegisterAsWoozleHelper()metode statis , dan memanggil semua metode yang ditemukannya dengan panggilan balik yang dapat mereka gunakan untuk menceritakan Woozletentang diri mereka sendiri, menghindari perlu menggunakan Refleksi sementara mis deserializing data.
supercat
9

Seperti namanya sendiri itu mencerminkan apa yang dimilikinya misalnya metode kelas, dll selain menyediakan fitur untuk memanggil metode yang membuat instance secara dinamis saat runtime.

Ini digunakan oleh banyak kerangka kerja dan aplikasi di bawah kayu untuk memohon layanan tanpa benar-benar mengetahui kode.

Mohammed Sarfaraz
sumber
7

Refleksi memberi Anda kemampuan untuk menulis kode yang lebih umum. Ini memungkinkan Anda untuk membuat objek saat runtime dan memanggil metodenya saat runtime. Karenanya program dapat dibuat sangat parameter. Hal ini juga memungkinkan introspeksi objek dan kelas untuk mendeteksi variabel dan metodenya yang terpapar ke dunia luar.

saumik gupta
sumber
6

Reflectionmemiliki banyak kegunaan . Yang saya lebih akrab dengan, adalah untuk dapat membuat kode dengan cepat.

IE: kelas dinamis, fungsi, konstruktor - berdasarkan pada data apa pun (hasil xml / array / sql / hardcoded / dll.)

Ess Kay
sumber
1
Jawaban ini akan jauh lebih baik jika Anda memberikan hanya satu contoh kode yang dihasilkan baik dari hasil SQL atau file XML dll.
ThisClark
Tidak masalah. Saya menggunakan refleksi dalam aplikasi windows yang secara dinamis menghasilkan antarmuka berdasarkan XML yang diambil dari database.
Ess Kay
Jadi pada dasarnya, ada kelas yang saya buat yang menunjukkan laporan kepada pengguna. Laporan ini memiliki parameter seperti Tanggal (dari ke) id, atau apa pun lainnya. Info ini disimpan dalam xml. Jadi pertama-tama kita punya pilihan laporan. Berdasarkan laporan yang dipilih, formulir mendapatkan xml. Setelah xml diambil, ia menggunakan refleksi untuk membuat kelas dengan bidang berdasarkan tipe yang direfleksikan. Setelah Anda mengubah ke laporan yang berbeda, sabak dihapus dan bidang-bidang baru dihasilkan berdasarkan xml. Jadi pada dasarnya bentuk dinamis berdasarkan refleksi. Saya juga menggunakan cara-cara lain tetapi ini seharusnya menjadi Harapan yang mencukupi
Ess Kay
3

Saya ingin menjawab pertanyaan ini dengan contoh. Pertama-tama Hibernateproyek menggunakan Reflection APIuntuk menghasilkan CRUDpernyataan untuk menjembatani jurang antara aplikasi yang berjalan dan toko kegigihan. Ketika hal-hal berubah dalam domain, mereka Hibernateharus tahu tentang mereka untuk bertahan mereka ke penyimpanan data dan sebaliknya.

Atau berfungsi Lombok Project. Itu hanya menyuntikkan kode pada waktu kompilasi, menghasilkan kode yang dimasukkan ke dalam kelas domain Anda. (Saya pikir itu OK untuk getter dan setter)

Hibernatememilih reflectionkarena memiliki dampak minimal pada proses pembuatan aplikasi.

Dan dari Java 7 yang kita miliki MethodHandles, yang berfungsi sebagai Reflection API. Dalam proyek, untuk bekerja dengan penebang, kami tinggal menyalin-tempel kode selanjutnya:

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

Karena sulit untuk membuat kesalahan ketik dalam kasus ini.

BSeitkazin
sumber
3

Ketika saya menemukan yang terbaik untuk menjelaskan dengan contoh dan tidak ada jawaban yang tampaknya melakukan itu ...

Contoh praktis menggunakan refleksi adalah Server Bahasa Jawa yang ditulis dalam Java atau Server Bahasa PHP yang ditulis dalam PHP, dll. Server Bahasa memberikan kemampuan IDE Anda seperti pelengkapan otomatis, lompat ke definisi, bantuan konteks, tipe petunjuk dan banyak lagi. Untuk memiliki semua nama tag (kata-kata yang dapat dilengkapi secara otomatis) untuk menampilkan semua kemungkinan yang cocok saat Anda mengetik Server Bahasa harus memeriksa segala sesuatu tentang kelas termasuk blok dokumen dan anggota pribadi. Untuk itu perlu refleksi dari kelas tersebut.

Contoh yang berbeda akan menjadi unit-test dari metode pribadi. Salah satu cara untuk melakukannya adalah dengan membuat refleksi dan mengubah ruang lingkup metode kepada publik dalam fase persiapan tes. Tentu saja orang dapat berargumen bahwa metode pribadi tidak boleh diuji secara langsung tetapi bukan itu intinya.

cprn
sumber