JavaRush /Java Blog /Random-TL /Reflection API. Pagninilay. Ang Madilim na Gilid ng Java
Oleksandr Klymenko
Antas
Харків

Reflection API. Pagninilay. Ang Madilim na Gilid ng Java

Nai-publish sa grupo
Pagbati, batang Padawan. Sa artikulong ito sasabihin ko sa iyo ang tungkol sa Force, ang kapangyarihan kung saan ginagamit lamang ng mga java programmer sa isang tila walang pag-asa na sitwasyon. Kaya, ang madilim na bahagi ng Java ay -Reflection API
Reflection API.  Pagninilay.  Ang Madilim na Gilid ng Java - 1
Ang pagninilay sa Java ay ginagawa gamit ang Java Reflection API. Ano ang repleksyon na ito? Mayroong maikli at tumpak na kahulugan na sikat din sa Internet. Ang Reflection (mula sa Late Latin reflexio - going back) ay isang mekanismo para sa pag-aaral ng data tungkol sa isang programa sa panahon ng pagpapatupad nito. Binibigyang-daan ka ng Reflection na suriin ang impormasyon tungkol sa mga field, pamamaraan, at mga constructor ng klase. Ang mekanismo ng pagmuni-muni mismo ay nagpapahintulot sa iyo na iproseso ang mga uri na wala sa panahon ng pag-compile, ngunit lumitaw sa panahon ng pagpapatupad ng programa. Ang pagmuni-muni at ang pagkakaroon ng isang lohikal na pare-parehong modelo para sa pag-uulat ng mga error ay ginagawang posible na lumikha ng tamang dynamic na code. Sa madaling salita, ang pag-unawa sa kung paano gumagana ang reflection sa java ay nagbubukas ng maraming kamangha-manghang pagkakataon para sa iyo. Maaari mong literal na salamangkahin ang mga klase at ang kanilang mga bahagi.
Reflection API.  Pagninilay.  Ang Madilim na Gilid ng Java - 2
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 ng klase, field, pamamaraan, constants, constructor at superclass;
  • Alamin kung aling mga pamamaraan ang nabibilang sa ipinatupad na interface/mga interface;
  • Lumikha ng isang halimbawa ng isang klase, at ang pangalan ng klase ay hindi alam hanggang sa ang programa ay naisakatuparan;
  • Kunin at itakda ang halaga ng isang object field 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 kung ang Java bilang isang platform ay maaaring nakamit ang napakalaking pag-aampon nang walang pagmuni-muni. Malamang hindi ko kaya. Naging pamilyar ka sa pangkalahatang teoretikal na ideya ng pagmuni-muni, ngayon ay bumaba tayo sa praktikal na aplikasyon nito! Hindi namin pag-aaralan ang lahat ng mga pamamaraan ng Reflection API, kung ano lamang ang aktwal na nakatagpo sa pagsasanay. Dahil ang mekanismo ng pagmuni-muni ay nagsasangkot ng pagtatrabaho sa mga klase, magkakaroon tayo ng isang simpleng klase - 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 natin, ito ang pinakakaraniwang klase. Ang constructor na may mga parameter ay nagkomento para sa isang dahilan, babalik kami dito mamaya. Kung titingnan mong mabuti ang mga nilalaman ng klase, malamang na nakita mo ang kawalan ng getter'a para sa name. Ang mismong field nameay minarkahan ng isang access modifier private; hindi namin ito maa-access sa labas ng klase mismo; =>hindi namin makuha ang halaga nito. "So anong problema? - sabi mo. "Idagdag gettero baguhin ang modifier ng access." At magiging tama ka, ngunit paano kung MyClassito ay nasa isang pinagsama-samang aklatan ng aar o sa isa pang saradong module nang walang pag-access sa pag-edit, at sa pagsasanay ito ay madalas na nangyayari. At ang ilang hindi nag-iingat na programmer ay nakalimutang magsulat getter. Panahon na upang tandaan ang tungkol sa pagmuni-muni! Subukan nating makarating sa privatefield nameng klase MyClass:
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
}
Alamin natin kung ano ang nangyari dito ngayon. Mayroong isang kahanga - hangang klase sa java Class. Kinakatawan nito ang mga klase at interface sa isang executable na Java application. Hindi namin hawakan ang koneksyon sa pagitan Classng at ClassLoader. hindi ito ang paksa ng artikulo. Susunod, para makuha ang mga field ng klase na ito, kailangan mong tawagan ang method getFields(), ibabalik sa amin ng paraang ito ang lahat ng available na field ng klase. Ito ay hindi angkop para sa amin, dahil ang aming field ay private, kaya ginagamit namin ang pamamaraan getDeclaredFields(). Ang pamamaraang ito ay nagbabalik din ng isang hanay ng mga patlang ng klase, ngunit ngayon pareho privateat protected. Sa aming sitwasyon, alam namin ang pangalan ng field na interesado sa amin, at maaari naming gamitin ang paraan getDeclaredField(String), kung saan Stringang pangalan ng nais na field. Tandaan: getFields()at getDeclaredFields()huwag ibalik ang mga patlang ng parent class! Mahusay, nakatanggap kami ng isang bagay Field na may link sa aming name. kasi ang field ay hindi публичным(pampubliko), dapat bigyan ng access upang gumana dito. Ang pamamaraan setAccessible(true)ay nagpapahintulot sa amin na magpatuloy sa pagtatrabaho. Ngayon ang field nameay ganap na sa ilalim ng aming kontrol! Maaari mong makuha ang halaga nito sa pamamagitan ng pagtawag get(Object)sa object Field, kung saan Objectang isang instance ng aming klase MyClass. Inihagis namin ito Stringat itinalaga ito sa aming variable name. Kung sakaling biglang wala kaming setter'a, maaari naming gamitin ang paraan upang magtakda ng bagong halaga para sa field ng pangalan set:
field.set(myClass, (String) "new value");
Binabati kita! Kakabisado mo lang ang pangunahing mekanismo ng pagmuni-muni at na-access mo ang privatefield! Bigyang-pansin ang bloke try/catchat mga uri ng mga pagbubukod na pinangangasiwaan. Ang IDE mismo ay magsasaad ng kanilang ipinag-uutos na presensya, ngunit nilinaw ng kanilang pangalan kung bakit sila naririto. Sige lang! Tulad ng maaaring napansin mo, ang sa amin MyClassay mayroon nang paraan 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 legacy dito. Ang pamamaraan ay nasa ilalim ng access modifier private, at kinailangan naming isulat ang output code sa bawat oras. Hindi ito maayos, nasaan ang ating repleksyon?... Isulat natin 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();
   }
}
Narito ang pamamaraan ay humigit-kumulang kapareho ng pagkuha ng isang patlang - nakukuha namin ang nais na paraan ayon sa pangalan at binibigyan kami ng access dito. At upang tawagan ang bagay Methodna ginagamit namin invoke(Оbject, Args), kung saan Оbjectay din ng isang halimbawa ng klase MyClass. Args- mga argumento ng pamamaraan - ang sa amin ay wala. Ngayon ginagamit namin ang function upang ipakita ang impormasyon printData:
public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; //?
   printData(myClass); // outout 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, mayroon na tayong access sa pribadong paraan ng klase. Ngunit paano kung ang pamamaraan ay mayroon pa ring mga argumento, at bakit mayroong isang nagkomento na tagabuo? Lahat ng bagay ay may kanya kanyang oras. Mula sa kahulugan sa simula ay malinaw na ang pagmuni-muni ay nagpapahintulot sa iyo na lumikha ng mga pagkakataon ng isang klase sa isang mode runtime(habang tumatakbo ang programa)! Maaari tayong lumikha ng isang bagay ng isang klase sa pamamagitan ng ganap na kwalipikadong pangalan ng klase na iyon. Ang ganap na kwalipikadong pangalan ng klase ay ang pangalan ng klase, na ibinigay ang path dito sa package.
Reflection API.  Pagninilay.  Ang Madilim na Gilid ng Java - 3
Sa aking hierarchy, packageang buong pangalan MyClassay magiging " reflection.MyClass". Maaari mo ring malaman ang pangalan ng klase sa isang simpleng paraan (ibabalik nito ang pangalan ng klase bilang isang string):
MyClass.class.getName()
Gumawa tayo ng isang instance ng klase gamit ang reflection:
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
}
Sa oras na magsimula ang java application, hindi lahat ng klase ay na-load sa JVM. Kung ang iyong code ay hindi tumutukoy sa class MyClass, kung gayon ang taong responsable sa pag-load ng mga klase sa JVM, at iyon ay ClassLoader, ay hinding-hindi ito ilo-load doon. Samakatuwid, kailangan nating pilitin ClassLoaderitong mag-load at makatanggap ng paglalarawan ng ating klase sa anyo ng isang variable ng uri Class. Para sa gawaing ito, mayroong isang paraan forName(String), kung saan Stringang pangalan ng klase na ang paglalarawan ay kailangan namin. Kapag natanggap , babalik Сlassang tawag sa pamamaraan , na gagawin ayon sa parehong paglalarawan. Ito ay nananatiling dalhin ang bagay na ito sa aming klase . Malamig! Ito ay mahirap, ngunit umaasa ako na ito ay naiintindihan. Ngayon ay maaari na tayong lumikha ng isang instance ng isang klase na literal mula sa isang linya! Sa kasamaang palad, ang inilarawan na pamamaraan ay gagana lamang sa default na tagabuo (nang walang mga parameter). Paano tumawag sa mga pamamaraan na may mga argumento at mga konstruktor na may mga parameter? Oras na para i-uncomment ang aming constructor. Tulad ng inaasahan, hindi nito mahanap ang default na tagabuo at hindi na gumagana. Isulat muli natin ang paglikha ng isang halimbawa ng klase: newInstance()ObjectMyClassnewInstance()
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
}
Upang makakuha ng mga constructor ng klase, tawagan ang pamamaraan mula sa paglalarawan ng klase getConstructors(), at upang makakuha ng mga parameter ng constructor, tawagan ang getParameterTypes():
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
   Class[] paramTypes = constructor.getParameterTypes();
   for (Class paramType : paramTypes) {
       System.out.print(paramType.getName() + " ");
   }
   System.out.println();
}
Sa ganitong paraan nakukuha namin ang lahat ng mga konstruktor at lahat ng mga parameter sa kanila. Sa aking halimbawa, mayroong isang tawag sa isang tiyak na tagabuo na may tiyak, alam na mga parameter. At upang tawagan ang tagabuo na ito, ginagamit namin ang pamamaraan newInstance, kung saan tinukoy namin ang mga halaga para sa mga parameter na ito. Ang parehong mangyayari invokepara sa mga pamamaraan ng pagtawag. Ang tanong ay lumitaw: saan maaaring maging kapaki-pakinabang ang mapanimdim na pagtawag sa mga konstruktor? Ang mga modernong teknolohiya ng java, tulad ng nabanggit sa simula, ay hindi magagawa nang wala ang Reflection API. Halimbawa, ang DI (Dependency Injection), kung saan ang mga annotation na sinamahan ng reflection ng mga pamamaraan at constructor ay bumubuo sa Dagger library, na sikat sa Android development. Pagkatapos basahin ang artikulong ito, maaari mong kumpiyansa na isaalang-alang ang iyong sarili na maliwanagan sa mga mekanismo ng Reflection API. Ito ay hindi para sa wala na ang pagmuni-muni ay tinatawag na madilim na bahagi ng java. Ito ay ganap na sinisira ang OOP paradigm. Sa java, ang encapsulation ay nagsisilbing itago at limitahan ang pag-access ng ilang bahagi ng programa sa iba. Sa pamamagitan ng paggamit ng pribadong modifier, ang ibig naming sabihin ay ang pag-access sa field na ito ay nasa loob lamang ng klase kung saan umiiral ang field na ito, batay dito, binuo namin ang karagdagang arkitektura ng programa. Sa artikulong ito, nakita namin kung paano mo magagamit ang pagmuni-muni upang makarating kahit saan. Ang isang magandang halimbawa sa anyo ng isang solusyon sa arkitektura ay ang generative na pattern ng disenyo - Singleton. Ang pangunahing ideya nito ay sa buong operasyon ng programa, ang klase na nagpapatupad ng template na ito ay dapat magkaroon lamang ng isang kopya. Ginagawa ito sa pamamagitan ng pagtatakda ng default na access modifier sa pribado para sa constructor. At magiging napakasama kung ang ilang programmer na may sariling pagmuni-muni ay lumilikha ng gayong mga klase. Sa pamamagitan ng paraan, mayroong isang napaka-kagiliw-giliw na tanong na narinig ko kamakailan mula sa aking empleyado: maaari bang magkaroon ng mga Singletontagapagmana ang isang klase na nagpapatupad ng isang template? Posible bang kahit na ang pagmuni-muni ay walang kapangyarihan sa kasong ito? Isulat ang iyong puna sa artikulo at ang sagot sa mga komento, at itanong din ang iyong mga katanungan! Ang tunay na kapangyarihan ng Reflection API ay kasama ng Runtime Annotation, na malamang na pag-uusapan natin sa hinaharap na artikulo tungkol sa madilim na bahagi ng Java. Salamat sa iyong atensyon!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION