CodeGym /Blog Jawa /Acak /Tuladha refleksi
John Squirrels
tingkat
San Francisco

Tuladha refleksi

Diterbitake ing grup
Mungkin sampeyan wis nemoni konsep "refleksi" ing urip biasa. Tembung iki biasane nuduhake proses sinau dhewe. Ing pemrograman, nduweni makna sing padha - yaiku mekanisme kanggo nganalisa data babagan program, lan malah ngganti struktur lan prilaku program, nalika program kasebut mlaku. Tuladha refleksi - 1 Sing penting ing kene yaiku kita nindakake iki nalika runtime, dudu ing wektu kompilasi. Nanging kenapa mriksa kode nalika runtime? Sawise kabeh, sampeyan wis bisa maca kode kasebut: / Ana alesan kenapa ide refleksi bisa uga ora jelas: nganti saiki, sampeyan mesthi ngerti kelas sing digunakake. Contone, sampeyan bisa nulis Catkelas:

package learn.codegym;

public class Cat {

   private String name;
   private int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public void sayMeow() {

       System.out.println("Meow!");
   }

   public void jump() {

       System.out.println("Jump!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

@Override
public String toString() {
   return "Cat{" +
           "name='" + name + '\'' +
           ", age=" + age +
           '}';
}

}
Sampeyan ngerti kabeh babagan iki, lan sampeyan bisa ndeleng lapangan lan cara sing diduweni. Umpamane sampeyan kudu ngenalake kelas kewan liyane menyang program kasebut. Sampeyan mbokmenawa bisa nggawe struktur warisan kelas karo Animalkelas induk kanggo penak. Sadurungé, kita malah nggawe kelas makili klinik Veterinary, kang kita bisa pass obyek Animal(kayata kelas tiyang sepah), lan program dianggep kewan jumbuh adhedhasar apa iku asu utawa kucing. Sanajan iki dudu tugas sing paling gampang, program kasebut bisa sinau kabeh informasi sing dibutuhake babagan kelas ing wektu kompilasi. Patut, nalika sampeyan pass Catobyek kanggo cara saka kelas Clinic Veterinary ingmain()cara, program wis ngerti sing iku kucing, ora asu. Saiki ayo bayangake yen kita ngadhepi tugas sing beda. Tujuan kita yaiku nulis penganalisa kode. Kita kudu nggawe CodeAnalyzerkelas kanthi cara siji: void analyzeObject(Object o). Cara iki kudu:
  • nemtokake kelas obyek liwati lan nampilake jeneng kelas ing console;
  • nemtokake jeneng kabeh lapangan kelas liwati, kalebu pribadi, lan nampilake ing console;
  • nemtokake jeneng kabeh cara saka kelas liwati, kalebu pribadi, lan nampilake ing console.
Bakal katon kaya iki:

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
      
       // Print the name of the class of object o
       // Print the names of all variables of this class
       // Print the names of all methods of this class
   }
  
}
Saiki kita bisa ndeleng kanthi jelas kepiye tugas iki beda karo tugas liyane sing wis dirampungake sadurunge. Kanthi tujuan kita saiki, kangelan dumunung ing kasunyatan manawa kita utawa program ora ngerti apa sing bakal diterusake menyanganalyzeClass()cara. Yen sampeyan nulis program kuwi, programer liyane bakal miwiti nggunakake, lan padha bisa pass apa-apa kanggo cara iki - sembarang kelas Jawa standar utawa kelas liyane padha nulis. Kelas sing wis lulus bisa duwe sawetara variabel lan metode. Kanthi tembung liyane, kita (lan program kita) ora ngerti kelas apa sing bakal kita lakoni. Nanging isih, kita kudu ngrampungake tugas iki. Lan ing kene API Refleksi Java standar mbantu kita. API Refleksi minangka alat basa sing kuat. Dokumentasi resmi Oracle nyaranake mekanisme iki mung bisa digunakake dening programer sing berpengalaman sing ngerti apa sing ditindakake. Sampeyan bakal ngerti apa sebabe kita menehi bebaya kaya iki luwih dhisik :) Mangkene dhaptar perkara sing bisa ditindakake nganggo API Refleksi:
  1. Ngenali / nemtokake kelas obyek.
  2. Entuk informasi babagan modifiers kelas, kolom, metode, konstanta, konstruktor, lan superclass.
  3. Temokake cara sing kalebu antarmuka sing diimplementasikake.
  4. Nggawe conto kelas sing jeneng kelas ora dikenal nganti program dieksekusi.
  5. Entuk lan setel nilai lapangan conto kanthi jeneng.
  6. Nelpon cara conto kanthi jeneng.
Daftar nyengsemaken, huh? :) Cathetan:mekanisme bayangan bisa nindakake kabeh iki "ing fly", preduli saka jinis obyek kita pass kanggo analyzer kode kita! Ayo njelajah kemampuan Reflection API kanthi ndeleng sawetara conto.

Carane ngenali / nemtokake kelas obyek

Ayo dadi miwiti karo dhasar. Titik entri menyang mesin refleksi Jawa yaiku kelas Class. Ya, iku katon tenan lucu, nanging sing apa bayangan :) Nggunakake kelas Class, kita pisanan nemtokake kelas sembarang obyek liwati kanggo cara kita. Ayo nyoba nindakake iki:

import learn.codegym.Cat;

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
       Class clazz = o.getClass();
       System.out.println(clazz);
   }

   public static void main(String[] args) {

       analyzeClass(new Cat("Fluffy", 6));
   }
}
Output konsol:

class learn.codegym.Cat
Pay manungsa waé kanggo rong perkara. Kaping pisanan, kita sengaja nyelehake Catkelas kasebut ing learn.codegympaket sing kapisah. Saiki sampeyan bisa ndeleng manawa getClass()metode kasebut ngasilake jeneng lengkap kelas kasebut. Kapindho, kita jenenge variabel kita clazz. Sing katon rada aneh. Mesthi wae yen diarani "kelas", nanging "kelas" minangka tembung sing dilindhungi ing basa Jawa. Compiler ora ngidini variabel kasebut diarani. Kita kudu ngubengi sing piye wae :) Ora ala kanggo wiwitan! Apa maneh sing ana ing dhaptar kapabilitas kasebut?

Carane entuk informasi babagan modifiers kelas, kolom, metode, konstanta, konstruktor, lan superclasses.

Saiki samubarang dadi luwih menarik! Ing kelas saiki, kita ora duwe konstanta utawa kelas induk. Ayo ditambahake kanggo nggawe gambar lengkap. Nggawe Animalkelas induk sing paling gampang:

package learn.codegym;
public class Animal {

   private String name;
   private int age;
}
Lan kita bakal nggawe Catkelas warisan Animallan nambah siji konstanta:

package learn.codegym;

public class Cat extends Animal {

   private static final String ANIMAL_FAMILY = "Feline family";

   private String name;
   private int age;

   // ...the rest of the class
}
Saiki kita duwe gambar lengkap! Ayo ndeleng refleksi apa sing bisa ditindakake :)

import learn.codegym.Cat;

import java.util.Arrays;

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
       Class clazz = o.getClass();
       System.out.println("Class name: " + clazz);
       System.out.println("Class fields: " + Arrays.toString(clazz.getDeclaredFields()));
       System.out.println("Parent class: " + clazz.getSuperclass());
       System.out.println("Class methods: " + Arrays.toString(clazz.getDeclaredMethods()));
       System.out.println("Class constructors: " + Arrays.toString(clazz.getConstructors()));
   }

   public static void main(String[] args) {

       analyzeClass(new Cat("Fluffy", 6));
   }
}
Mangkene apa sing kita deleng ing konsol:

Class name:  class learn.codegym.Cat 
Class fields: [private static final java.lang.String learn.codegym.Cat.ANIMAL_FAMILY, private java.lang.String learn.codegym.Cat.name, private int learn.codegym.Cat.age] 
Parent class: class learn.codegym.Animal 
Class methods: [public java.lang.String learn.codegym.Cat.getName(), public void learn.codegym.Cat.setName(java.lang.String), public void learn.codegym.Cat.sayMeow(), public void learn.codegym.Cat.setAge(int), public void learn.codegym.Cat.jump(), public int learn.codegym.Cat.getAge()] 
Class constructors: [public learn.codegym.Cat(java.lang.String, int)]
Deleng kabeh informasi kelas sing rinci sing bisa dipikolehi! Lan ora mung informasi umum, nanging uga informasi pribadi! Cathetan: privatevariabel uga ditampilake ing dhaptar. "Analisis" kelas kita bisa dianggep pancen lengkap: kita nggunakake analyzeObject()metode kanggo sinau kabeh sing bisa. Nanging iki ora kabeh sing bisa ditindakake kanthi refleksi. Kita ora diwatesi mung pengamatan prasaja - kita bakal nerusake kanggo njupuk tindakan! :)

Carane nggawe conto saka kelas kang jeneng kelas ora dikenal nganti program wis kaleksanan.

Ayo dadi miwiti karo konstruktor standar. Kelas kita Catdurung duwe, mula ditambahake:

public Cat() {
  
}
Iki kode kanggo nggawe Catobyek nggunakake refleksi ( createCat()metode):

import learn.codegym.Cat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

   public static Cat createCat() throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
       String className = reader.readLine();

       Class clazz = Class.forName(className);
       Cat cat = (Cat) clazz.newInstance();

       return cat;
   }

public static Object createObject() throws Exception {

   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
   String className = reader.readLine();

   Class clazz = Class.forName(className);
   Object result = clazz.newInstance();

   return result;
}

   public static void main(String[] args) throws IOException, IllegalAccessException, ClassNotFoundException, InstantiationException {
       System.out.println(createCat());
   }
}
Input konsol:

learn.codegym.Cat
Output konsol:

Cat{name='null', age=0}
Iki ora kesalahan: nilai namelan ageditampilake ing console amarga kita wrote kode kanggo output ing toString()cara saka Catkelas. Ing kene kita maca jeneng kelas sing obyek bakal digawe saka console. Program kasebut ngenali jeneng kelas sing obyek bakal digawe. Tuladha refleksi - 3Kanggo ringkesan, kita ngilangi kode penanganan pangecualian sing tepat, sing mbutuhake papan luwih akeh tinimbang conto kasebut. Ing program nyata, mesthi, sampeyan kudu nangani kahanan nglibatno salah ngetik jeneng, etc. Konstruktor gawan cukup prasaja, supaya sampeyan bisa ndeleng, iku gampang kanggo nggunakake kanggo nggawe Kayata saka kelas :) Nggunakake newInstance()cara , kita nggawe obyek anyar saka kelas iki. Iku prakara liyane yen ingCatkonstruktor njupuk argumen minangka input. Ayo mbusak konstruktor standar kelas lan nyoba kanggo mbukak kode maneh.

null
java.lang.InstantiationException: learn.codegym.Cat 
at java.lang.Class.newInstance(Class.java:427)
Ana sing salah! Kita entuk kesalahan amarga kita nelpon cara kanggo nggawe obyek nggunakake konstruktor standar. Nanging saiki kita ora duwe konstruktor kaya ngono. Dadi nalika newInstance()metode kasebut mlaku, mekanisme refleksi nggunakake konstruktor lawas kita kanthi rong parameter:

public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}
Nanging kita ora nindakake apa-apa karo paramèter, kaya-kaya kita wis lali kabeh! Nggunakake refleksi kanggo ngirim argumen menyang konstruktor mbutuhake "kreativitas":

import learn.codegym.Cat;

import java.lang.reflect.InvocationTargetException;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;

       try {
           clazz = Class.forName("learn.codegym.Cat");
           Class[] catClassParams = {String.class, int.class};
           cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Fluffy", 6);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }

       return cat;
   }

   public static void main(String[] args) {
       System.out.println(createCat());
   }
}
Output konsol:

Cat{name='Fluffy', age=6}
Ayo dideleng kanthi cetha apa sing kedadeyan ing program kita. Kita nggawe macem-macem Classobyek.

Class[] catClassParams = {String.class, int.class};
Padha cocog karo paramèter konstruktor kita (sing mung duwe Stringlan intparamèter). We pass menyang clazz.getConstructor()cara lan entuk akses menyang konstruktor dikarepake. Sawise iku, kabeh sing kudu kita lakoni yaiku nelpon newInstance()metode kasebut kanthi argumen sing dibutuhake, lan aja lali masang obyek kasebut kanthi jelas menyang jinis sing dikarepake: Cat.

cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Fluffy", 6);
Saiki obyek kita wis kasil digawe! Output konsol:

Cat{name='Fluffy', age=6}
Langsung mlaku :)

Cara entuk lan nyetel nilai lapangan conto kanthi jeneng.

Bayangake yen sampeyan nggunakake kelas sing ditulis dening programmer liyane. Kajaba iku, sampeyan ora duwe kemampuan kanggo nyunting. Contone, perpustakaan kelas siap-digawe rangkep ing JAR a. Sampeyan bisa maca kode saka kelas, nanging sampeyan ora bisa ngganti. Upaminipun programmer sing nggawe salah siji saka kelas ing perpustakaan iki (ayo dadi Catkelas lawas kita), gagal kanggo njaluk cukup turu ing wayah wengi sadurunge desain wis rampung, dibusak getter lan setter kanggo lapangan age. Saiki kelas iki wis teka kanggo sampeyan. Iki nyukupi kabeh kabutuhan, amarga sampeyan mung butuh Catobyek ing program sampeyan. Nanging sampeyan kudu duwe agelapangan! Iki masalah: kita ora bisa tekan lapangan, amarga wisprivatemodifier, lan getter lan setter dibusak dening pangembang turu sing nggawe kelas: / Inggih, bayangan bisa mbantu kita ing kahanan iki! Kita duwe akses menyang kode kanggo Catkelas, supaya kita bisa ngerti paling lapangan apa lan apa padha disebut. Bersenjata karo informasi iki, kita bisa ngatasi masalah kita:

import learn.codegym.Cat;

import java.lang.reflect.Field;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;
       try {
           clazz = Class.forName("learn.codegym.Cat");
           cat = (Cat) clazz.newInstance();

           // We got lucky with the name field, since it has a setter
           cat.setName("Fluffy");

           Field age = clazz.getDeclaredField("age");
          
           age.setAccessible(true);

           age.set(cat, 6);

       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (NoSuchFieldException e) {
           e.printStackTrace();
       }

       return cat;
   }

   public static void main(String[] args) {
       System.out.println(createCat());
   }
}
Kaya sing kasebut ing komentar, kabeh sing ana ing namelapangan iku gampang, amarga pangembang kelas nyedhiyakake setter. Sampeyan wis ngerti carane nggawe obyek saka konstruktor standar: kita duwe newInstance()kanggo iki. Nanging kita kudu nindakake sawetara tinkering karo lapangan kapindho. Ayo ngerteni apa sing kedadeyan ing kene :)

Field age = clazz.getDeclaredField("age");
Ing kene, nggunakake Class clazzobyek kita, kita ngakses agelapangan liwat getDeclaredField()metode kasebut. Ngidini kita entuk lapangan umur minangka Field ageobyek. Nanging iki ora cukup, amarga kita ora bisa mung menehi nilai menyang privatekolom. Kanggo nindakake iki, kita kudu nggawe lapangan bisa diakses kanthi nggunakake setAccessible()metode:

age.setAccessible(true);
Sawise nindakake iki menyang lapangan, kita bisa nemtokake nilai:

age.set(cat, 6);
Nalika sampeyan bisa ndeleng, obyek kita Field ageduwe jinis setter nang-metu sing kita pass nilai int lan obyek kang lapangan bakal diutus. Kita mbukak main()metode lan ndeleng:

Cat{name='Fluffy', age=6}
Banget! Kita nindakaken! :) Ayo ndeleng apa maneh sing bisa ditindakake ...

Cara nelpon metode conto kanthi jeneng.

Ayo rada ngganti kahanan ing conto sadurunge. Ayo ngomong yen Catpangembang kelas ora nggawe kesalahan karo getter lan setter. Kabeh oke ing babagan iki. Saiki masalahe beda: ana cara sing mesthi dibutuhake, nanging pangembang nggawe pribadi:

private void sayMeow() {

   System.out.println("Meow!");
}
Iki tegese yen kita nggawe Catobyek ing program kita, kita ora bakal bisa nelpon sayMeow()cara ing wong. Kita bakal duwe kucing sing ora meow? Sing aneh : / Kepiye carane ndandani iki? Sawise maneh, API Refleksi mbantu kita! Kita ngerti jeneng metode sing dibutuhake. Kabeh liyane iku teknis:

import learn.codegym.Cat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {

   public static void invokeSayMeowMethod()  {

       Class clazz = null;
       Cat cat = null;
       try {

           cat = new Cat("Fluffy", 6);
          
           clazz = Class.forName(Cat.class.getName());
          
           Method sayMeow = clazz.getDeclaredMethod("sayMeow");
          
           sayMeow.setAccessible(true);
          
           sayMeow.invoke(cat);
          
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }
   }

   public static void main(String[] args) {
       invokeSayMeowMethod();
   }
}
Ing kene kita nindakake perkara sing padha karo sing ditindakake nalika ngakses lapangan pribadi. Kaping pisanan, kita entuk metode sing dibutuhake. Iku encapsulated ing Methodobyek:

Method sayMeow = clazz.getDeclaredMethod("sayMeow");
Cara kasebut getDeclaredMethod()ngidini kita menyang metode pribadi. Sabanjure, kita nggawe cara sing bisa diarani:

sayMeow.setAccessible(true);
Lan pungkasane, kita nelpon metode ing obyek sing dikarepake:

sayMeow.invoke(cat);
Ing kene, telpon metode kita katon kaya "panggilan maneh": kita wis biasa nggunakake titik kanggo ngarahake obyek menyang metode sing dikarepake ( cat.sayMeow()), nanging nalika nggarap refleksi, kita pindhah menyang metode obyek sing arep kita nelpon. metode kasebut. Apa sing ana ing konsol kita?

Meow!
Kabeh bisa! :) Saiki sampeyan bisa ndeleng kemungkinan akeh sing mekanisme bayangan Jawa menehi kita. Ing kahanan sing angel lan ora dikarepke (kayata conto kita karo kelas saka perpustakaan sing ditutup), pancen bisa mbantu kita akeh. Nanging, kaya kekuwatan gedhe, tanggung jawab kasebut uga gedhe. Kerugian refleksi diterangake ing bagean khusus ing situs web Oracle. Ana telung kekurangan utama:
  1. Kinerja luwih elek. Metode sing diarani nggunakake refleksi nduweni kinerja sing luwih elek tinimbang metode sing diarani kanthi cara normal.

  2. Ana watesan keamanan. Mekanisme refleksi ngidini kita ngganti prilaku program nalika runtime. Nanging ing papan kerja, nalika nggarap proyek nyata, sampeyan bisa ngadhepi watesan sing ora ngidini.

  3. Risiko paparan informasi internal. Penting kanggo ngerti refleksi minangka pelanggaran langsung saka prinsip enkapsulasi: ngidini kita ngakses lapangan pribadi, metode, lan liya-liyane. mung ing kasus sing paling ekstrim, nalika ora ana cara liya kanggo ngatasi masalah kanthi alasan sing ora bisa dikontrol.

Gunakake refleksi kanthi wicaksana lan mung ing kahanan sing ora bisa dihindari, lan aja lali babagan kekurangane. Kanthi iki, pelajaran kita wis rampung. Ternyata cukup suwe, nanging sampeyan sinau akeh dina iki :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION