CodeGym/Java Blog/சீரற்ற/பிரதிபலிப்பு API: பிரதிபலிப்பு. ஜாவாவின் இருண்ட பக்கம்
John Squirrels
நிலை 41
San Francisco

பிரதிபலிப்பு API: பிரதிபலிப்பு. ஜாவாவின் இருண்ட பக்கம்

சீரற்ற குழுவில் வெளியிடப்பட்டது
members
வாழ்த்துக்கள், இளம் படவன். இந்த கட்டுரையில், ஜாவா புரோகிராமர்கள் சாத்தியமற்ற சூழ்நிலைகளில் மட்டுமே பயன்படுத்தும் சக்தியைப் பற்றி நான் உங்களுக்கு சொல்கிறேன். ஜாவாவின் இருண்ட பக்கம் பிரதிபலிப்பு API ஆகும். ஜாவாவில், ஜாவா பிரதிபலிப்பு API ஐப் பயன்படுத்தி பிரதிபலிப்பு செயல்படுத்தப்படுகிறது.

ஜாவா பிரதிபலிப்பு என்றால் என்ன?

இணையத்தில் ஒரு குறுகிய, துல்லியமான மற்றும் பிரபலமான வரையறை உள்ளது. பிரதிபலிப்பு ( லேட் லத்தீன் reflexio - to turn back ) என்பது ஒரு நிரல் இயங்கும் போது அது பற்றிய தரவை ஆராய்வதற்கான ஒரு பொறிமுறையாகும். புலங்கள், முறைகள் மற்றும் வகுப்புக் கட்டமைப்பாளர்கள் பற்றிய தகவல்களை ஆராய பிரதிபலிப்பு உங்களை அனுமதிக்கிறது. தொகுக்கும் நேரத்தில் இல்லாத, ஆனால் இயங்கும் நேரத்தில் கிடைக்கக்கூடிய வகைகளுடன் பணிபுரிய பிரதிபலிப்பு உங்களை அனுமதிக்கிறது. பிரதிபலிப்பு மற்றும் பிழைத் தகவலை வழங்குவதற்கான தர்க்கரீதியாக நிலையான மாதிரியானது சரியான டைனமிக் குறியீட்டை உருவாக்குவதை சாத்தியமாக்குகிறது. வேறு வார்த்தைகளில் கூறுவதானால், ஜாவாவில் பிரதிபலிப்பு எவ்வாறு செயல்படுகிறது என்பதைப் புரிந்துகொள்வது உங்களுக்கு பல அற்புதமான வாய்ப்புகளைத் திறக்கும். நீங்கள் உண்மையில் வகுப்புகள் மற்றும் அவற்றின் கூறுகளை ஏமாற்றலாம். பிரதிபலிப்பு என்ன அனுமதிக்கிறது என்பதற்கான அடிப்படை பட்டியல் இங்கே:
  • ஒரு பொருளின் வகுப்பைக் கற்றுக்கொள்/தீர்மானித்தல்;
  • ஒரு வகுப்பின் மாற்றிகள், புலங்கள், முறைகள், மாறிலிகள், கன்ஸ்ட்ரக்டர்கள் மற்றும் சூப்பர்கிளாஸ்கள் பற்றிய தகவலைப் பெறுங்கள்;
  • செயல்படுத்தப்பட்ட இடைமுகம்(களுக்கு) என்ன முறைகள் உள்ளன என்பதைக் கண்டறியவும்;
  • இயக்க நேரம் வரை வகுப்பின் பெயர் தெரியாத வகுப்பின் உதாரணத்தை உருவாக்கவும்;
  • ஒரு பொருளின் புலங்களின் மதிப்புகளை பெயரால் பெற்று அமைக்கவும்;
  • ஒரு பொருளின் முறையை பெயரால் அழைக்கவும்.
கிட்டத்தட்ட அனைத்து நவீன ஜாவா தொழில்நுட்பங்களிலும் பிரதிபலிப்பு பயன்படுத்தப்படுகிறது. ஜாவா, ஒரு தளமாக, பிரதிபலிப்பு இல்லாமல் இத்தகைய பரவலான தத்தெடுப்பை அடைந்திருக்க முடியும் என்று கற்பனை செய்வது கடினம். பெரும்பாலும், அது இருக்காது. இப்போது நீங்கள் பொதுவாக பிரதிபலிப்பு ஒரு தத்துவார்த்த கருத்தாக அறிந்திருக்கிறீர்கள், அதன் நடைமுறை பயன்பாட்டிற்கு செல்லலாம்! பிரதிபலிப்பு ஏபிஐயின் அனைத்து முறைகளையும் நாங்கள் கற்றுக்கொள்ள மாட்டோம்—நடைமுறையில் நீங்கள் சந்திக்கும் முறைகள் மட்டுமே. பிரதிபலிப்பு என்பது வகுப்புகளுடன் வேலை செய்வதை உள்ளடக்கியதால், நாங்கள் ஒரு எளிய வகுப்பில் தொடங்குவோம் 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);
   }
}
நீங்கள் பார்க்க முடியும் என, இது மிகவும் அடிப்படை வகுப்பு. அளவுருக்கள் கொண்ட கட்டமைப்பாளர் வேண்டுமென்றே கருத்து தெரிவிக்கிறார். அதற்குப் பிறகு வருவோம். வகுப்பின் உள்ளடக்கங்களை நீங்கள் கவனமாகப் பார்த்தீர்கள் என்றால், பெயர் புலத்திற்கான பெறுநர் இல்லாததை நீங்கள் கவனித்திருக்கலாம் . பெயர் புலம் தனிப்பட்ட அணுகல் மாற்றியமைப்புடன் குறிக்கப்பட்டுள்ளது : வகுப்பிற்கு வெளியே எங்களால் அதை அணுக முடியாது, அதாவது அதன் மதிப்பை எங்களால் மீட்டெடுக்க முடியாது . " அதனால் என்ன பிரச்சனை ?" நீ சொல்கிறாய். "ஒரு பெறுநரைச் சேர்க்கவும் அல்லது அணுகல் மாற்றியை மாற்றவும்". நீங்கள் சரியாக இருப்பீர்கள், இல்லையெனில்MyClassதொகுக்கப்பட்ட AAR நூலகத்தில் அல்லது மாற்றங்களைச் செய்யும் திறன் இல்லாத மற்றொரு தனியார் தொகுதியில் இருந்தது. நடைமுறையில், இது எல்லா நேரத்திலும் நடக்கும். மேலும் சில கவனக்குறைவான புரோகிராமர்கள் ஒரு பெறுபேற்றை எழுத மறந்துவிட்டார்கள் . பிரதிபலிப்பை நினைவில் கொள்ள வேண்டிய நேரம் இது! வகுப்பின் தனிப்பட்ட பெயர் புலத்திற்குச் செல்ல முயற்சிப்போம் 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
}
என்ன நடந்தது என்பதை பகுப்பாய்வு செய்வோம். ஜாவாவில், ஒரு அற்புதமான வகுப்பு உள்ளது Class. இது இயங்கக்கூடிய ஜாவா பயன்பாட்டில் வகுப்புகள் மற்றும் இடைமுகங்களைக் குறிக்கிறது. Classமற்றும் இடையே உள்ள உறவை நாங்கள் மறைக்க மாட்டோம் ClassLoader, ஏனெனில் அது இந்தக் கட்டுரையின் தலைப்பு அல்ல. அடுத்து, இந்த வகுப்பின் புலங்களை மீட்டெடுக்க, நீங்கள் getFields()முறையை அழைக்க வேண்டும். இந்த முறை இந்த வகுப்பின் அணுகக்கூடிய புலங்கள் அனைத்தையும் வழங்கும். இது எங்களுக்கு வேலை செய்யாது, ஏனெனில் எங்கள் புலம் தனிப்பட்டது , எனவே நாங்கள் முறையைப் பயன்படுத்துகிறோம் getDeclaredFields(). இந்த முறை வகுப்பு புலங்களின் வரிசையையும் வழங்குகிறது, ஆனால் இப்போது அது தனிப்பட்ட மற்றும் பாதுகாக்கப்பட்ட புலங்களை உள்ளடக்கியது. இந்த வழக்கில், நாங்கள் ஆர்வமாக உள்ள துறையின் பெயரை நாங்கள் அறிவோம், எனவே நாங்கள் முறையைப் getDeclaredField(String)பயன்படுத்தலாம்Stringவிரும்பிய புலத்தின் பெயர். குறிப்பு: getFields()மேலும் getDeclaredFields()பெற்றோர் வகுப்பின் புலங்களைத் திருப்பித் தர வேண்டாம்! நன்று. Fieldஎங்கள் பெயரைக் குறிப்பிடும் ஒரு பொருள் கிடைத்தது . புலம் பொதுவில் இல்லாததால் , அதனுடன் பணிபுரிய அனுமதி வழங்க வேண்டும். முறை setAccessible(true)மேலும் தொடர உதவுகிறது. இப்போது பெயர் புலம் எங்கள் முழு கட்டுப்பாட்டில் உள்ளது! Fieldபொருளின் முறையை அழைப்பதன் மூலம் அதன் மதிப்பை நீங்கள் மீட்டெடுக்கலாம் get(Object), அங்கு Objectஎங்கள் வகுப்பின் உதாரணம் உள்ளது MyClass. வகையை மாற்றி , நமது பெயர்String மாறிக்கு மதிப்பை ஒதுக்குகிறோம் . பெயர் புலத்தில் புதிய மதிப்பை அமைக்க செட்டரைக் கண்டுபிடிக்க முடியவில்லை என்றால் , நீங்கள் செட் முறையைப் பயன்படுத்தலாம்:
field.set(myClass, (String) "new value");
வாழ்த்துகள்! நீங்கள் பிரதிபலிப்பு அடிப்படைகளில் தேர்ச்சி பெற்றுள்ளீர்கள் மற்றும் தனிப்பட்ட துறையை அணுகியுள்ளீர்கள்! try/catchதொகுதி மற்றும் கையாளப்படும் விதிவிலக்குகளின் வகைகளுக்கு கவனம் செலுத்துங்கள் . அவர்களின் இருப்பு தேவை என்பதை IDE உங்களுக்குத் தெரிவிக்கும், ஆனால் அவர்கள் ஏன் இங்கு இருக்கிறார்கள் என்பதை அவர்களின் பெயர்களால் நீங்கள் தெளிவாகக் கூறலாம். நகர்கிறது! நீங்கள் கவனித்தபடி, எங்கள் MyClassவகுப்பில் ஏற்கனவே வகுப்புத் தரவைப் பற்றிய தகவலைக் காண்பிக்கும் முறை உள்ளது:
private void printData(){
       System.out.println(number + name);
   }
ஆனால் இந்த புரோகிராமர் தனது கைரேகையை இங்கேயும் விட்டுவிட்டார். இந்த முறை ஒரு தனிப்பட்ட அணுகல் மாற்றியைக் கொண்டுள்ளது, மேலும் ஒவ்வொரு முறையும் தரவைக் காண்பிக்க எங்கள் சொந்த குறியீட்டை எழுத வேண்டும். என்ன ஒரு குழப்பம். நமது பிரதிபலிப்பு எங்கே போனது? பின்வரும் செயல்பாட்டை எழுதவும்:
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();
   }
}
இங்குள்ள செயல்முறையானது புலத்தை மீட்டெடுப்பதற்குப் பயன்படுத்தப்படும் செயல்முறையைப் போன்றது. நாங்கள் விரும்பிய முறையை பெயரால் அணுகி அதற்கான அணுகலை வழங்குகிறோம். மற்றும் Methodபொருளின் மீது நாம் invoke(Object, Args)முறை என்று அழைக்கிறோம், அங்கு Objectவகுப்பின் ஒரு உதாரணமும் உள்ளது MyClass. Argsமுறையின் வாதங்கள், எங்களுடையது இல்லை என்றாலும். printDataஇப்போது தகவலைக் காண்பிக்க செயல்பாட்டைப் பயன்படுத்துகிறோம் :
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
}
ஹர்ரே! இப்போது வகுப்பின் தனிப்பட்ட முறைக்கான அணுகலைப் பெற்றுள்ளோம். ஆனால் முறைக்கு வாதங்கள் இருந்தால் என்ன செய்வது, மற்றும் கட்டமைப்பாளர் ஏன் கருத்து தெரிவிக்கிறார்? எல்லாம் அதன் சொந்த நேரத்தில். ரன் நேரத்தில் (நிரல் இயங்கும் போது) ஒரு வகுப்பின் நிகழ்வுகளை உருவாக்க பிரதிபலிப்பு உங்களை அனுமதிக்கிறது என்பது தொடக்கத்தில் உள்ள வரையறையிலிருந்து தெளிவாகிறது ! வகுப்பின் முழுப் பெயரைப் பயன்படுத்தி ஒரு பொருளை உருவாக்கலாம். வகுப்பின் முழுப் பெயர் அதன் தொகுப்பின் பாதை உட்பட வகுப்பின் பெயராகும் .
பிரதிபலிப்பு API: பிரதிபலிப்பு.  ஜாவாவின் இருண்ட பக்கம் - 2
எனது தொகுப்பு படிநிலையில், MyClass இன் முழுப் பெயர் "reflection.MyClass" என்று இருக்கும். வகுப்பின் பெயரைக் கற்றுக்கொள்வதற்கான எளிய வழியும் உள்ளது (வகுப்பின் பெயரை ஒரு சரமாகத் திருப்பி விடுங்கள்):
MyClass.class.getName()
வகுப்பின் நிகழ்வை உருவாக்க ஜாவா பிரதிபலிப்பைப் பயன்படுத்துவோம்:
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
}
ஜாவா பயன்பாடு தொடங்கும் போது, ​​அனைத்து வகுப்புகளும் JVM இல் ஏற்றப்படாது. MyClassஉங்கள் குறியீடு வகுப்பைக் குறிக்கவில்லை என்றால் ClassLoader, JVM இல் வகுப்புகளை ஏற்றுவதற்குப் பொறுப்பான , வகுப்பை ஏற்றாது. அதாவது, நீங்கள் ClassLoaderஅதை ஏற்றுவதற்கு கட்டாயப்படுத்த வேண்டும் மற்றும் ஒரு மாறி வடிவில் ஒரு வகுப்பு விளக்கத்தைப் பெற வேண்டும் Class. இதனால்தான் எங்களிடம் forName(String)முறை உள்ளது, Stringஅதன் விளக்கம் நமக்குத் தேவையான வகுப்பின் பெயர் எங்கே. பொருளைப் பெற்ற பிறகு Сlass, முறையை அழைப்பது அந்த விளக்கத்தைப் பயன்படுத்தி உருவாக்கப்பட்ட newInstance()ஒரு பொருளைத் திருப்பித் தரும் Object. இந்த பொருளை எங்களுக்கு வழங்குவது மட்டுமே எஞ்சியுள்ளதுMyClassவர்க்கம். குளிர்! அது கடினமாக இருந்தது, ஆனால் புரிந்துகொள்ளக்கூடியதாக இருந்தது, நான் நம்புகிறேன். இப்போது நாம் ஒரு வகுப்பின் உதாரணத்தை ஒரு வரியில் உருவாக்கலாம்! துரதிர்ஷ்டவசமாக, விவரிக்கப்பட்ட அணுகுமுறை இயல்புநிலை கட்டமைப்பாளருடன் மட்டுமே செயல்படும் (அளவுருக்கள் இல்லாமல்). அளவுருக்கள் கொண்ட முறைகள் மற்றும் கட்டமைப்பாளர்களை எப்படி அழைப்பது? எங்கள் கட்டமைப்பாளரைக் கருத்துத் தெரிவிக்க வேண்டிய நேரம் இது. எதிர்பார்த்தபடி, newInstance()இயல்புநிலை கட்டமைப்பாளரைக் கண்டுபிடிக்க முடியவில்லை, இனி வேலை செய்யாது. கிளாஸ் இன்ஸ்டண்டியேஷனை மீண்டும் எழுதுவோம்:
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
}
வகுப்பு கட்டமைப்பாளர்களைப் பெறுவதற்கு இந்த getConstructors()முறையானது வர்க்க வரையறையில் அழைக்கப்பட வேண்டும், பின்னர் 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();
}
இது அனைத்து கட்டமைப்பாளர்களையும் அவற்றின் அளவுருக்களையும் பெறுகிறது. எனது எடுத்துக்காட்டில், குறிப்பிட்ட, முன்னர் அறியப்பட்ட அளவுருக்கள் கொண்ட ஒரு குறிப்பிட்ட கட்டமைப்பாளரை நான் குறிப்பிடுகிறேன். இந்த கட்டமைப்பாளரை அழைக்க, newInstanceஇந்த அளவுருக்களின் மதிப்புகளை அனுப்பும் முறையைப் பயன்படுத்துகிறோம். invokeஅழைப்பு முறைகளைப் பயன்படுத்தும்போதும் இதுவே இருக்கும் . இது கேள்வியைக் கேட்கிறது: பிரதிபலிப்பு மூலம் கட்டமைப்பாளர்களை அழைப்பது எப்போது பயனுள்ளதாக இருக்கும்? ஆரம்பத்தில் ஏற்கனவே குறிப்பிட்டுள்ளபடி, நவீன ஜாவா தொழில்நுட்பங்கள் ஜாவா பிரதிபலிப்பு API இல்லாமல் பெற முடியாது. எடுத்துக்காட்டாக, டிபென்டென்சி இன்ஜெக்ஷன் (DI), இது குறிப்புகளை முறைகள் மற்றும் கட்டமைப்பாளர்களின் பிரதிபலிப்புடன் ஒருங்கிணைத்து பிரபலமான டேரரை உருவாக்குகிறது.ஆண்ட்ராய்டு மேம்பாட்டிற்கான நூலகம். இந்தக் கட்டுரையைப் படித்த பிறகு, ஜாவா ரிஃப்ளெக்ஷன் ஏபிஐயின் வழிகளில் நீங்கள் படித்தவர் என்று நீங்கள் நம்பிக்கையுடன் கருதலாம். அவர்கள் பிரதிபலிப்பை ஜாவாவின் இருண்ட பக்கமாக அழைப்பதில்லை. இது OOP முன்னுதாரணத்தை முற்றிலும் உடைக்கிறது. ஜாவாவில், என்காப்சுலேஷன் சில நிரல் கூறுகளுக்கு மற்றவர்களின் அணுகலை மறைக்கிறது மற்றும் கட்டுப்படுத்துகிறது. நாங்கள் தனிப்பட்ட மாற்றியைப் பயன்படுத்தும் போது, ​​அந்த புலத்தை அது இருக்கும் வகுப்பிற்குள் இருந்து மட்டுமே அணுக வேண்டும் என்று எண்ணுகிறோம். இந்த கொள்கையின் அடிப்படையில் நிரலின் அடுத்தடுத்த கட்டமைப்பை நாங்கள் உருவாக்குகிறோம். இந்த கட்டுரையில், எங்கும் உங்கள் வழியை கட்டாயப்படுத்த நீங்கள் பிரதிபலிப்பைப் பயன்படுத்துவது எப்படி என்பதைப் பார்த்தோம். படைப்பு வடிவமைப்பு முறை சிங்கிள்டன்ஒரு கட்டடக்கலை தீர்வாக இது ஒரு நல்ல உதாரணம். அடிப்படை யோசனை என்னவென்றால், இந்த முறையை செயல்படுத்தும் ஒரு வர்க்கம் முழு நிரலையும் செயல்படுத்தும் போது ஒரு நிகழ்வு மட்டுமே இருக்கும். இயல்புநிலை கட்டமைப்பாளருடன் தனிப்பட்ட அணுகல் மாற்றியைச் சேர்ப்பதன் மூலம் இது நிறைவேற்றப்படுகிறது. ஒரு புரோகிராமர் பிரதிபலிப்பைப் பயன்படுத்தினால் அது மிகவும் மோசமானதாக இருக்கும். மூலம், சமீபத்தில் ஒரு சக பணியாளர் ஒரு சுவாரஸ்யமான கேள்வியைக் கேட்டேன்: சிங்கிள்டன் முறையைச் செயல்படுத்தும் ஒரு வகுப்பை மரபுரிமையாகப் பெற முடியுமா? இந்த விஷயத்தில், பிரதிபலிப்பு கூட சக்தியற்றதாக இருக்க முடியுமா? கீழே உள்ள கருத்துகளில் கட்டுரை மற்றும் உங்கள் பதிலைப் பற்றிய உங்கள் கருத்தை இடுங்கள், மேலும் உங்கள் சொந்த கேள்விகளைக் கேளுங்கள்!
கருத்துக்கள்
  • பிரபலமானவை
  • புதியவை
  • பழையவை
ஒரு கருத்தைத் தெரிவிக்க நீங்கள் உள்நுழைந்திருக்க வேண்டும்
இந்தப் பக்கத்தில் இதுவரை எந்தக் கருத்தும் வழங்கப்படவில்லை