CodeGym /Java Blog /Random /Reflection API: Reflection. Ang madilim na bahagi ng Java...
John Squirrels
Antas
San Francisco

Reflection API: Reflection. Ang madilim na bahagi ng Java

Nai-publish sa grupo
Pagbati, batang Padawan. Sa artikulong ito, sasabihin ko sa iyo ang tungkol sa Force, isang kapangyarihan na ginagamit lamang ng mga programmer ng Java sa mga tila imposibleng sitwasyon. Ang madilim na bahagi ng Java ay ang Reflection API. Sa Java, ipinapatupad ang reflection gamit ang Java Reflection API.

Ano ang Java reflection?

Mayroong maikli, tumpak, at tanyag na kahulugan sa Internet. Ang Reflection ( mula sa late Latin reflexio - to turn back ) ay isang mekanismo upang tuklasin ang data tungkol sa isang programa habang ito ay tumatakbo. Hinahayaan ka ng Reflection na tuklasin ang impormasyon tungkol sa mga field, pamamaraan, at constructor ng klase. Hinahayaan ka ng Reflection na magtrabaho sa mga uri na wala sa oras ng pag-compile, ngunit naging available sa oras ng pagtakbo. Ang pagmuni-muni at isang lohikal na pare-parehong modelo para sa pagbibigay ng impormasyon ng error ay ginagawang posible na lumikha ng tamang dynamic na code. Sa madaling salita, ang pag-unawa kung paano gumagana ang pagmuni-muni sa Java ay magbubukas ng maraming kamangha-manghang pagkakataon para sa iyo. Maaari mong literal na salamangkahin ang mga klase at ang kanilang mga bahagi. Narito ang isang pangunahing listahan ng kung ano ang pinapayagan ng pagmuni-muni:
  • Alamin / tukuyin ang klase ng isang bagay;
  • Kumuha ng impormasyon tungkol sa mga modifier, field, pamamaraan, constant, constructor, at superclass ng isang klase;
  • Alamin kung anong mga pamamaraan ang nabibilang sa (mga) ipinatupad na interface;
  • Lumikha ng isang instance ng isang klase na ang pangalan ng klase ay hindi alam hanggang sa oras ng pagtakbo;
  • Kumuha at magtakda ng mga halaga ng mga patlang ng isang bagay ayon sa pangalan;
  • Tumawag ng paraan ng isang bagay sa pamamagitan ng pangalan.
Ginagamit ang Reflection sa halos lahat ng modernong teknolohiya ng Java. Mahirap isipin na ang Java, bilang isang platform, ay maaaring nakamit ang ganoong kalat na pag-aampon nang walang pagmuni-muni. Malamang, hindi ito magkakaroon. Ngayon na sa pangkalahatan ay pamilyar ka sa pagmuni-muni bilang isang teoretikal na konsepto, magpatuloy tayo sa praktikal na aplikasyon nito! Hindi namin matututuhan ang lahat ng mga pamamaraan ng Reflection API—ang mga talagang makakaharap mo sa pagsasanay. Dahil ang pagmumuni-muni ay nagsasangkot ng pagtatrabaho sa mga klase, magsisimula tayo sa isang simpleng klase na tinatawag na MyClass:

public class MyClass {
   private int number;
   private String name = "default";
//    public MyClass(int number, String name) {
//        this.number = number;
//        this.name = name;
//    }
   public int getNumber() {
       return number;
   }
   public void setNumber(int number) {
       this.number = number;
   }
   public void setName(String name) {
       this.name = name;
   }
   private void printData(){
       System.out.println(number + name);
   }
}
Tulad ng nakikita mo, ito ay isang napakapangunahing klase. Ang constructor na may mga parameter ay sadyang nagkomento. Balikan natin yan mamaya. Kung titingnan mong mabuti ang mga nilalaman ng klase, malamang na napansin mo ang kawalan ng getter para sa field ng pangalan . Ang mismong field ng pangalan ay minarkahan ng modifier ng pribadong pag-access: hindi namin ito ma-access sa labas ng klase mismo na nangangahulugang hindi namin makuha ang halaga nito. " So anong problema ?" sabi mo. "Magdagdag ng getter o baguhin ang access modifier". At magiging tama ka, maliban kungMyClassay nasa isang pinagsama-samang library ng AAR o sa isa pang pribadong module na walang kakayahang gumawa ng mga pagbabago. Sa pagsasagawa, ito ay nangyayari sa lahat ng oras. At ang ilang pabaya na programmer ay nakalimutan lamang na magsulat ng isang getter . Ito ang mismong oras para alalahanin ang pagmuni-muni! Subukan nating pumunta sa private name field ng MyClassklase:

public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; // No getter =(
   System.out.println(number + name); // Output: 0null
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(number + name); // Output: 0default
}
Pag-aralan natin kung ano ang nangyari. Sa Java, mayroong isang kahanga-hangang klase na tinatawag na Class. Kinakatawan nito ang mga klase at interface sa isang executable na Java application. Hindi namin sasaklawin ang relasyon sa pagitan Classng at ClassLoader, dahil hindi iyon ang paksa ng artikulong ito. Susunod, upang makuha ang mga field ng klase na ito, kailangan mong tawagan ang getFields()pamamaraan. Ibabalik ng paraang ito ang lahat ng naa-access na field ng klase na ito. Hindi ito gumagana para sa amin, dahil ang aming field ay pribado , kaya ginagamit namin ang getDeclaredFields()pamamaraan. Ang pamamaraang ito ay nagbabalik din ng isang hanay ng mga patlang ng klase, ngunit ngayon ay kasama na ang pribado at protektadong mga patlang. Sa kasong ito, alam namin ang pangalan ng field kung saan kami interesado, para magamit namin ang getDeclaredField(String)paraan, kung saanStringay ang nais na pangalan ng field. Tandaan: getFields()at getDeclaredFields()huwag ibalik ang mga patlang ng isang klase ng magulang! Malaki. Mayroon kaming isang Fieldbagay na tumutukoy sa aming pangalan . Dahil hindi pampubliko ang field , dapat kaming magbigay ng access para magtrabaho dito. Hinahayaan tayo ng setAccessible(true)pamamaraan na magpatuloy pa. Ngayon ang field ng pangalan ay nasa ilalim ng aming kumpletong kontrol! Maaari mong makuha ang halaga nito sa pamamagitan ng pagtawag sa pamamaraan Fieldng object , kung saan mayroong isang instance ng aming klase. Kino-convert namin ang uri sa at itinalaga ang halaga sa aming variable ng pangalan . Kung hindi kami makahanap ng setter upang magtakda ng bagong value sa field ng pangalan, maaari mong gamitin ang set na paraan: get(Object)ObjectMyClassString

field.set(myClass, (String) "new value");
Binabati kita! Kakabisado mo lang ang mga pangunahing kaalaman sa pagmuni-muni at na-access mo ang isang pribadong field! Bigyang-pansin ang try/catchbloke, at ang mga uri ng mga pagbubukod na hinahawakan. Sasabihin sa iyo ng IDE na ang kanilang presensya ay kinakailangan sa sarili nitong, ngunit malinaw mong masasabi sa kanilang mga pangalan kung bakit sila naririto. Moving on! Tulad ng maaaring napansin mo, ang aming MyClassklase ay mayroon nang pamamaraan para sa pagpapakita ng impormasyon tungkol sa data ng klase:

private void printData(){
       System.out.println(number + name);
   }
Ngunit ang programmer na ito ay nag-iwan din ng kanyang mga fingerprint dito. Ang pamamaraan ay may pribadong access modifier, at kailangan naming isulat ang aming sariling code upang magpakita ng data sa bawat oras. Ang gulo. Saan napunta ang ating repleksyon? Isulat ang sumusunod na function:

public static void printData(Object myClass){
   try {
       Method method = myClass.getClass().getDeclaredMethod("printData");
       method.setAccessible(true);
       method.invoke(myClass);
   } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
       e.printStackTrace();
   }
}
Ang pamamaraan dito ay halos kapareho ng ginamit para sa pagkuha ng isang field. Ina-access namin ang gustong paraan ayon sa pangalan at binibigyan namin ng access ito. At sa Methodbagay na tinatawag nating invoke(Object, Args)pamamaraan, kung saan Objectay isang instance din ng MyClassklase. Argsay ang mga argumento ng pamamaraan, kahit na ang sa amin ay wala. Ngayon ginagamit namin ang printDatafunction upang ipakita ang impormasyon:

public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; //?
   printData(myClass); // Output: 0default
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       field.set(myClass, (String) "new value");
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   printData(myClass);// Output: 0new value
}
Hurray! Ngayon ay mayroon na tayong access sa pribadong pamamaraan ng klase. Ngunit paano kung ang pamamaraan ay may mga argumento, at bakit ang tagabuo ay nagkomento? Lahat sa sarili nitong takdang panahon. Ito ay malinaw mula sa kahulugan sa simula na ang pagmuni-muni ay nagbibigay-daan sa iyong lumikha ng mga pagkakataon ng isang klase sa oras ng pagtakbo (habang tumatakbo ang programa)! Maaari tayong lumikha ng isang bagay gamit ang buong pangalan ng klase. Ang buong pangalan ng klase ay ang pangalan ng klase, kasama ang path ng package nito .
Reflection API: Reflection.  Ang madilim na bahagi ng Java - 2
Sa aking package hierarchy, ang buong pangalan ng MyClass ay magiging "reflection.MyClass". Mayroon ding simpleng paraan para matutunan ang pangalan ng klase (ibalik ang pangalan ng klase bilang String):

MyClass.class.getName()
Gamitin natin ang Java reflection upang lumikha ng isang instance ng klase:

public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       myClass = (MyClass) clazz.newInstance();
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(myClass); // Output: created object reflection.MyClass@60e53b93
}
Kapag nagsimula ang isang Java application, hindi lahat ng klase ay na-load sa JVM. Kung ang iyong code ay hindi tumutukoy sa MyClassklase, kung gayon ClassLoader, ang , na responsable sa pag-load ng mga klase sa JVM, ay hindi kailanman maglo-load sa klase. Nangangahulugan iyon na kailangan mong pilitin ClassLoaderna i-load ito at makakuha ng paglalarawan ng klase sa anyo ng isang Classvariable. Ito ang dahilan kung bakit mayroon tayong forName(String)pamamaraan, nasaan Stringang pangalan ng klase na ang paglalarawan ay kailangan natin. Matapos makuha ang Сlassbagay, ang pagtawag sa pamamaraan newInstance()ay magbabalik ng isang Objectbagay na nilikha gamit ang paglalarawang iyon. Ang natitira na lang ay ibigay ang bagay na ito sa amingMyClassklase. Malamig! Iyon ay mahirap, ngunit naiintindihan, umaasa ako. Ngayon ay maaari na tayong lumikha ng isang halimbawa ng isang klase sa literal na isang linya! Sa kasamaang palad, ang inilarawan na diskarte ay gagana lamang sa default na tagabuo (nang walang mga parameter). Paano mo tinatawag ang mga pamamaraan at konstruktor na may mga parameter? Oras na para i-uncomment ang aming constructor. Tulad ng inaasahan, newInstance()hindi mahanap ang default na tagabuo, at hindi na gumagana. Isulat muli natin ang instantiation ng klase:

public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       Class[] params = {int.class, String.class};
       myClass = (MyClass) clazz.getConstructor(params).newInstance(1, "default2");
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
       e.printStackTrace();
   }
   System.out.println(myClass);// Output: created object reflection.MyClass@60e53b93
}
Ang getConstructors()pamamaraan ay dapat na tawagan sa kahulugan ng klase upang makakuha ng mga konstruktor ng klase, at pagkatapos getParameterTypes()ay dapat na tawagan upang makakuha ng mga parameter ng isang tagabuo:

Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
   Class[] paramTypes = constructor.getParameterTypes();
   for (Class paramType : paramTypes) {
       System.out.print(paramType.getName() + " ");
   }
   System.out.println();
}
Nakukuha namin ang lahat ng mga konstruktor at ang kanilang mga parameter. Sa aking halimbawa, sumangguni ako sa isang tiyak na tagabuo na may tiyak, dati nang kilalang mga parameter. At upang tawagan ang tagabuo na ito, ginagamit namin ang newInstancepamamaraan, kung saan ipinapasa namin ang mga halaga ng mga parameter na ito. Magiging pareho ito kapag gumagamit invokeng mga paraan ng pagtawag. Ito ay nagtatanong: kailan ang pagtawag sa mga konstruktor sa pamamagitan ng pagmuni-muni ay madaling gamitin? Gaya ng nabanggit na sa simula, ang mga makabagong teknolohiya ng Java ay hindi makakamit kung wala ang Java Reflection API. Halimbawa, Dependency Injection (DI), na pinagsasama ang mga anotasyon sa pagmuni-muni ng mga pamamaraan at constructor upang mabuo ang sikat na Darerlibrary para sa pagpapaunlad ng Android. Pagkatapos basahin ang artikulong ito, maaari mong kumpiyansa na isaalang-alang ang iyong sarili na edukado sa mga paraan ng Java Reflection API. Hindi nila tinatawag ang reflection na madilim na bahagi ng Java para sa wala. Ito ay ganap na sinisira ang OOP paradigm. Sa Java, itinatago at pinaghihigpitan ng encapsulation ang access ng iba sa ilang partikular na bahagi ng program. Kapag ginamit namin ang pribadong modifier, nilalayon naming ma-access lang ang field na iyon mula sa loob ng klase kung saan ito umiiral. At binubuo namin ang kasunod na arkitektura ng programa batay sa prinsipyong ito. Sa artikulong ito, nakita namin kung paano mo magagamit ang pagmuni-muni upang pilitin ang iyong paraan kahit saan. Ang pattern ng paglikha ng disenyo na Singletonay isang magandang halimbawa nito bilang solusyon sa arkitektura. Ang pangunahing ideya ay ang isang klase na nagpapatupad ng pattern na ito ay magkakaroon lamang ng isang pagkakataon sa panahon ng pagpapatupad ng buong programa. Ito ay nagagawa sa pamamagitan ng pagdaragdag ng pribadong access modifier sa default na constructor. At magiging napakasama kung ang isang programmer ay gumamit ng pagmuni-muni o lumikha ng higit pang mga pagkakataon ng naturang mga klase. Oo nga pala, narinig ko kamakailan ang isang katrabaho na nagtanong ng isang napaka-kagiliw-giliw na tanong: maaari bang mamana ang isang klase na nagpapatupad ng pattern ng Singleton? Hindi kaya, sa kasong ito, kahit na ang pagmuni-muni ay walang kapangyarihan? Iwanan ang iyong feedback tungkol sa artikulo at ang iyong sagot sa mga komento sa ibaba, at magtanong ng sarili mong mga katanungan doon!

Higit pang pagbabasa:

Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION