रिफ्लेक्शन API कशासाठी आहे?

Java ची रिफ्लेक्शन मेकॅनिझम डेव्हलपरला बदल करू देते आणि वर्ग, इंटरफेस, फील्ड आणि पद्धतींबद्दल रनटाइमच्या वेळी त्यांची नावे जाणून घेतल्याशिवाय माहिती मिळवू देते.

रिफ्लेक्शन API तुम्हाला नवीन ऑब्जेक्ट्स, कॉल पद्धती आणि फील्ड व्हॅल्यू मिळवू किंवा सेट करू देते.

आपण प्रतिबिंब वापरून करू शकत असलेल्या प्रत्येक गोष्टीची सूची बनवूया:

  • ऑब्जेक्टचा वर्ग ओळखा/निर्धारित करा
  • वर्ग सुधारक, फील्ड, पद्धती, स्थिरांक, कन्स्ट्रक्टर आणि सुपरक्लास बद्दल माहिती मिळवा
  • कोणत्या पद्धती लागू केलेल्या इंटरफेसशी संबंधित आहेत ते शोधा
  • प्रोग्राम कार्यान्वित होईपर्यंत ज्या वर्गाचे नाव माहित नाही अशा वर्गाचे उदाहरण तयार करा
  • नावाने उदाहरण फील्डचे मूल्य मिळवा आणि सेट करा
  • नावाने एक उदाहरण पद्धत कॉल करा

जवळजवळ सर्व आधुनिक जावा तंत्रज्ञान प्रतिबिंब वापरतात. हे आजच्या बहुतेक Java / Java EE फ्रेमवर्क आणि लायब्ररींना अधोरेखित करते, उदाहरणार्थ:

  • वेब अनुप्रयोग तयार करण्यासाठी स्प्रिंग फ्रेमवर्क
  • JUnit चाचणी फ्रेमवर्क

जर तुम्हाला या यंत्रणेचा सामना कधीच झाला नसेल, तर तुम्ही कदाचित विचारत असाल की हे सर्व का आवश्यक आहे. उत्तर अगदी सोपे आहे पण अगदी अस्पष्ट देखील आहे: प्रतिबिंब नाटकीयरित्या लवचिकता आणि आमचा अनुप्रयोग आणि कोड सानुकूलित करण्याची क्षमता वाढवते.

परंतु नेहमीच साधक आणि बाधक असतात. तर काही बाधकांचा उल्लेख करूया:

  • अनुप्रयोग सुरक्षिततेचे उल्लंघन. प्रतिबिंब आम्हाला कोडमध्ये प्रवेश करू देते जे आम्ही करू नये (एनकॅप्सुलेशनचे उल्लंघन).
  • सुरक्षा निर्बंध. रिफ्लेक्शनला रनटाइम परवानग्या आवश्यक आहेत ज्या सुरक्षा व्यवस्थापक चालवणाऱ्या सिस्टमसाठी उपलब्ध नाहीत.
  • कमी कामगिरी. Java मधील रिफ्लेक्शन क्लासपाथ स्कॅन करून लोड करण्यासाठी क्लास शोधून डायनॅमिकली प्रकार निर्धारित करते . यामुळे प्रोग्रामची कार्यक्षमता कमी होते.
  • राखणे कठीण. प्रतिबिंब वापरणारा कोड वाचणे आणि डीबग करणे कठीण आहे. हे कमी लवचिक आणि देखरेख करणे कठीण आहे.

रिफ्लेक्शन API वापरून वर्गांसह कार्य करणे

सर्व रिफ्लेक्शन ऑपरेशन्स java.lang.Class ऑब्जेक्टने सुरू होतात. प्रत्येक प्रकारच्या ऑब्जेक्टसाठी, java.lang.Class चे अपरिवर्तनीय उदाहरण तयार केले जाते. हे ऑब्जेक्ट गुणधर्म मिळविण्यासाठी, नवीन ऑब्जेक्ट्स तयार करण्यासाठी आणि कॉलिंग पद्धती प्रदान करते.

java.lang.Class सह काम करण्याच्या मूलभूत पद्धतींची यादी पाहू :

पद्धत कृती
स्ट्रिंग getName(); वर्गाचे नाव परत करते
int getModifiers(); प्रवेश सुधारक परत करते
पॅकेज getPackage(); पॅकेजबद्दल माहिती मिळवते
वर्ग getSuperclass(); पालक वर्गाबद्दल माहिती मिळवते
वर्ग[] getInterfaces(); इंटरफेसची अॅरे मिळवते
कन्स्ट्रक्टर[] getConstructors(); क्लास कन्स्ट्रक्टरबद्दल माहिती मिळवते
फील्ड[] getFields(); वर्गाची फील्ड मिळवते
फील्ड getField(स्ट्रिंग फील्डनाव); नावानुसार वर्गाचे विशिष्ट फील्ड मिळवते
पद्धत[] getMethods(); पद्धतींचा अ‍ॅरे मिळवते

वर्ग, इंटरफेस, फील्ड आणि पद्धतींबद्दल डेटा मिळविण्यासाठी या सर्वात महत्वाच्या पद्धती आहेत. अशा पद्धती देखील आहेत ज्या तुम्हाला फील्ड व्हॅल्यू मिळवू किंवा सेट करू देतात आणि खाजगी फील्डमध्ये प्रवेश करू शकतात. आम्ही त्यांना थोड्या वेळाने पाहू.

आत्ता आपण java.lang.Class स्वतः मिळवण्याबद्दल बोलू . हे करण्याचे आमच्याकडे तीन मार्ग आहेत.

1. Class.forName वापरणे

चालू असलेल्या ऍप्लिकेशनमध्ये, तुम्ही क्लास मिळवण्यासाठी forName(String className) पद्धत वापरावी.

हा कोड आपण प्रतिबिंब वापरून वर्ग कसे तयार करू शकतो हे दाखवतो. चला एक व्यक्ती वर्ग तयार करू ज्यासह आपण कार्य करू शकतो:


package com.company;

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

आणि आमच्या उदाहरणाचा दुसरा भाग हा कोड आहे जो प्रतिबिंब वापरतो:


public class TestReflection {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("com.company.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

वर्गाचे पूर्ण नाव माहित असल्यास हा दृष्टिकोन शक्य आहे. मग तुम्ही static Class.forName() पद्धत वापरून संबंधित वर्ग मिळवू शकता. ही पद्धत आदिम प्रकारांसाठी वापरली जाऊ शकत नाही.

2. वर्ग वापरणे

जर टाईप उपलब्ध असेल पण त्याचे उदाहरण नसेल, तर टाईपच्या नावात .class जोडून तुम्ही क्लास मिळवू शकता. आदिम प्रकाराचा वर्ग मिळवण्याचा हा सर्वात सोपा मार्ग आहे.


Class aClass = Person.class;

3. .getClass() वापरणे

ऑब्जेक्ट उपलब्ध असल्यास, क्लास मिळवण्याचा सर्वात सोपा मार्ग म्हणजे object.getClass() वर कॉल करणे .


Person person = new Person();
Class aClass = person.getClass();

शेवटच्या दोन पद्धतींमध्ये काय फरक आहे?

कोडिंगच्या वेळी तुम्हाला कोणत्या वर्गाच्या ऑब्जेक्टमध्ये स्वारस्य आहे हे माहित असल्यास A.class वापरा . कोणतेही उदाहरण उपलब्ध नसल्यास, आपण .class वापरावे .

वर्गाच्या पद्धती मिळवणे

आमच्या वर्गाच्या पद्धती परत करणाऱ्या पद्धती पाहू: getDeclaredMethods() आणि getMethods() .

getDeclaredMethods() वर्गाच्या सर्व घोषित पद्धतींसाठी मेथड ऑब्जेक्ट्स किंवा क्लास ऑब्जेक्टद्वारे प्रस्तुत इंटरफेस, सार्वजनिक, खाजगी, डीफॉल्ट आणि संरक्षित पद्धतींसह, परंतु वारसा न मिळालेल्या पद्धतींचा समावेश असलेला अॅरे परत करतो .

getMethods() क्लासच्या सर्व सार्वजनिक पद्धतींसाठी किंवा क्लास ऑब्जेक्टद्वारे दर्शविल्या जाणार्‍या इंटरफेससाठी मेथड ऑब्जेक्ट्स असलेली अॅरे परत करते — ज्या क्लास किंवा इंटरफेसद्वारे घोषित केल्या जातात, तसेच सुपरक्लास आणि सुपरइंटरफेसमधून वारशाने मिळालेल्या .

त्यापैकी प्रत्येक कसे कार्य करते ते पाहू या.

चला getDeclaredMethods() ने सुरुवात करूया . दोन पद्धतींमधील फरक समजून घेण्यासाठी आम्हाला पुन्हा मदत करण्यासाठी, खाली आम्ही अमूर्त संख्या वर्गासह कार्य करू. चला एक स्थिर पद्धत लिहूया जी आमच्या मेथड अॅरेला List<String> मध्ये रूपांतरित करेल :


import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Method[] declaredMethods = Number.class.getDeclaredMethods();
        List<String> actualMethodNames = getMethodNames(declaredMethods);
        actualMethodNames.forEach(System.out::println);
    }

    private static List<String> getMethodNames(Method[] methods) {
        return Arrays.stream(methods)
                .map(Method::getName)
                .collect(Collectors.toList());
    }
}

हा कोड चालवण्याचा परिणाम येथे आहे:

byteValue
shortValue
intValue
longValue
float floatValue;
दुहेरी मूल्य

या संख्या वर्गात घोषित केलेल्या पद्धती आहेत . getMethods() काय परत करते? उदाहरणातील दोन ओळी बदलू.


final Method[] methods = Number.class.getMethods();
List<String> actualMethodNames = getMethodNames(methods);

असे केल्याने, आपण खालील पद्धतींचा संच पाहू.

byteValue
shortValue
intValue
longValue
float floatValue;
डबल व्हॅल्यू
प्रतीक्षा
प्रतीक्षा
प्रतीक्षा स्ट्रिंग हॅशकोड गेटक्लास नोटिफिकेशन नोटिफिक ऑल
बरोबर आहे




कारण सर्व वर्गांना ऑब्जेक्टचा वारसा मिळतो, आमची पद्धत ऑब्जेक्ट क्लासच्या सार्वजनिक पद्धती देखील परत करते .

वर्गाची फील्ड मिळवणे

वर्गाची फील्ड मिळविण्यासाठी getFields आणि getDeclaredFields पद्धती वापरल्या जातात. उदाहरण म्हणून, LocalDateTime वर्ग पाहू . आम्ही आमचा कोड पुन्हा लिहू:


import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Field[] declaredFields = LocalDateTime.class.getDeclaredFields();
        List<String> actualFieldNames = getFieldNames(declaredFields);
        actualFieldNames.forEach(System.out::println);
    }

    private static List<String> getFieldNames(Field[] fields) {
        return Arrays.stream(fields)
                .map(Field::getName)
                .collect(Collectors.toList());
    }
}

हा कोड कार्यान्वित केल्यामुळे, आम्हाला LocalDateTime वर्गामध्ये समाविष्ट असलेल्या फील्डचा संच मिळतो.

MIN
MAX
serialVersionUID
तारीख
वेळ

आमच्या मागील पद्धतींच्या परीक्षणाशी साधर्म्य ठेवून, जर आपण कोड थोडा बदलला तर काय होते ते पाहूया:


final Field[] fields = LocalDateTime.class.getFields();
List<String> actualFieldNames = getFieldNames(fields);

आउटपुट:

MIN
MAX

आता या पद्धतींमधील फरक शोधूया.

getDeclaredFields पद्धत वर्ग किंवा इंटरफेसद्वारे घोषित केलेल्या सर्व फील्डसाठी फील्ड ऑब्जेक्ट्सची अॅरे देतेवर्गवस्तू

getFields पद्धत वर्गाच्या सर्व सार्वजनिक फील्ड किंवा इंटरफेस द्वारे प्रस्तुत केलेल्या फील्ड ऑब्जेक्ट्सची अॅरे देतेवर्गवस्तू

आता LocalDateTime मध्ये पाहू .

वर्गाचामिआणिकमालफील्ड सार्वजनिक आहेत, याचा अर्थ ते getFields पद्धतीद्वारे दृश्यमान होतील . याउलट, दतारीख,वेळ,serialVersionUIDपद्धतींमध्ये खाजगी सुधारक असतो, याचा अर्थ ते getFields पद्धतीद्वारे दृश्यमान होणार नाहीत , परंतु आम्ही ते getDeclaredFields वापरून मिळवू शकतो . अशा प्रकारे आपण खाजगी फील्डसाठी फील्ड ऑब्जेक्ट्समध्ये प्रवेश करू शकतो.

इतर पद्धतींचे वर्णन

आता वर्ग वर्गाच्या काही पद्धतींबद्दल बोलण्याची वेळ आली आहे , म्हणजे:

पद्धत कृती
मॉडिफायर्स मिळवा आमच्या वर्गासाठी मॉडिफायर मिळवत आहे
गेटपॅकेज आमचे वर्ग असलेले पॅकेज मिळवणे
सुपरक्लास मिळवा पालक वर्ग मिळत आहे
इंटरफेस मिळवा वर्गाद्वारे लागू केलेल्या इंटरफेसचे अॅरे मिळवणे
getName पूर्ण पात्र वर्गाचे नाव मिळवणे
getSimpleName वर्गाचे नाव मिळवणे

getModifiers()

ए वापरून मॉडिफायर्समध्ये प्रवेश केला जाऊ शकतोवर्गवस्तू

मॉडिफायर्स हे पब्लिक , स्टॅटिक , इंटरफेस इ. सारखे कीवर्ड आहेत . आम्हाला getModifiers() पद्धत वापरून मॉडिफायर्स मिळतात :


Class<Person> personClass = Person.class;
int classModifiers = personClass.getModifiers();

हा कोड an चे मूल्य सेट करतोintव्हेरिएबल जे थोडे फील्ड आहे. प्रत्येक प्रवेश सुधारक संबंधित बिट सेट करून किंवा साफ करून चालू किंवा बंद केला जाऊ शकतो. आपण java.lang.reflect.Modifier वर्गातील पद्धती वापरून सुधारक तपासू शकतो :


import com.company.Person;
import java.lang.reflect.Modifier;

public class TestReflection {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        int classModifiers = personClass.getModifiers();

        boolean isPublic = Modifier.isPublic(classModifiers);
        boolean isStatic = Modifier.isStatic(classModifiers);
        boolean isFinal = Modifier.isFinal(classModifiers);
        boolean isAbstract = Modifier.isAbstract(classModifiers);
        boolean isInterface = Modifier.isInterface(classModifiers);

        System.out.printf("Class modifiers: %d%n", classModifiers);
        System.out.printf("Is public: %b%n", isPublic);
        System.out.printf("Is static: %b%n", isStatic);
        System.out.printf("Is final: %b%n", isFinal);
        System.out.printf("Is abstract: %b%n", isAbstract);
        System.out.printf("Is interface: %b%n", isInterface);
    }
}

आमच्या व्यक्तीची घोषणा कशी दिसते ते आठवा:


public class Person {
   …
}

आम्हाला खालील आउटपुट मिळते:

वर्ग सुधारक: 1
सार्वजनिक आहे: सत्य
स्थिर आहे: असत्य
अंतिम आहे: असत्य
आहे अमूर्त: असत्य
आहे इंटरफेस: असत्य

जर आपण आपला वर्ग अमूर्त बनवला तर आपल्याकडे आहे:


public abstract class Person { … }

आणि हे आउटपुट:

वर्ग सुधारक: 1025
सार्वजनिक आहे: सत्य
स्थिर आहे: असत्य
अंतिम आहे: असत्य
आहे अमूर्त: सत्य
आहे इंटरफेस: असत्य

आम्ही ऍक्सेस मॉडिफायर बदलला, याचा अर्थ आम्ही मॉडिफायर क्लासच्या स्टॅटिक पद्धतींद्वारे परत केलेला डेटा देखील बदलला .

getPackage()

केवळ एक वर्ग जाणून घेतल्यास, आम्ही त्याच्या पॅकेजबद्दल माहिती मिळवू शकतो:


Class<Person> personClass = Person.class;
final Package aPackage = personClass.getPackage();
System.out.println(aPackage.getName());

getSuperclass()

आमच्याकडे क्लास ऑब्जेक्ट असल्यास, आम्ही त्याच्या मूळ वर्गात प्रवेश करू शकतो:


public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<? super Person> superclass = personClass.getSuperclass();
    System.out.println(superclass);
}

आम्हाला सुप्रसिद्ध ऑब्जेक्ट क्लास मिळतो:


class java.lang.Object

परंतु जर आमच्या वर्गात दुसरा पालक वर्ग असेल, तर त्याऐवजी आम्ही ते पाहू:


package com.company;

class Human {
    // Some info
}

public class Person extends Human {
    private int age;
    private String name;

    // Some info
}

येथे आम्हाला आमचा पालक वर्ग मिळतो:


class com.company.Human

GetInterfaces()

वर्गाद्वारे लागू केलेल्या इंटरफेसची यादी आम्ही कशी मिळवू शकतो ते येथे आहे:


public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<?>[] interfaces = personClass.getInterfaces();
    System.out.println(Arrays.toString(interfaces));
}

आणि आमचा व्यक्ती वर्ग बदलण्यास विसरू नका :


public class Person implements Serializable { … }

आउटपुट:

[इंटरफेस java.io.Serializable]

एक वर्ग अनेक इंटरफेस लागू करू शकतो. म्हणूनच आम्हाला अॅरे मिळतातवर्गवस्तू. Java Reflection API मध्ये, इंटरफेस देखील द्वारे दर्शविले जातातवर्गवस्तू.

कृपया लक्षात ठेवा: पद्धत केवळ निर्दिष्ट वर्गाद्वारे अंमलात आणलेले इंटरफेस परत करते, त्याच्या पालक वर्गाने नाही. वर्गाद्वारे लागू केलेल्या इंटरफेसची संपूर्ण यादी मिळविण्यासाठी, तुम्हाला सध्याचा वर्ग आणि वारसा साखळीतील सर्व पूर्वजांचा संदर्भ घ्यावा लागेल.

getName() आणि getSimpleName() आणि getCanonicalName()

एक आदिम, नेस्टेड क्लास, एक अनामित वर्ग आणि स्ट्रिंग वर्ग यांचा समावेश असलेले उदाहरण लिहू :


public class TestReflection {
    public static void main(String[] args) {
        printNamesForClass(int.class, "int class (primitive)");
        printNamesForClass(String.class, "String.class (ordinary class)");
        printNamesForClass(java.util.HashMap.SimpleEntry.class,
                "java.util.HashMap.SimpleEntry.class (nested class)");
        printNamesForClass(new java.io.Serializable() {
                }.getClass(),
                "new java.io.Serializable(){}.getClass() (anonymous inner class)");
    }

    private static void printNamesForClass(final Class<?> clazz, final String label) {
        System.out.printf("%s:%n", label);
        System.out.printf("\tgetName()):\t%s%n", clazz.getName());
        System.out.printf("\tgetCanonicalName()):\t%s%n", clazz.getCanonicalName());
        System.out.printf("\tgetSimpleName()):\t%s%n", clazz.getSimpleName());
        System.out.printf("\tgetTypeName():\t%s%n%n", clazz.getTypeName());
    }
}

आमच्या कार्यक्रमाचा परिणाम:

int वर्ग (आदिम):
getName()): int
getCanonicalName()): int
getSimpleName()): int
getTypeName(): int

String.class (सामान्य वर्ग):
getName()): java.lang.String
getCanonicalName() ): java.lang.String
getSimpleName()): स्ट्रिंग
getTypeName(): java.lang.String

java.util.HashMap.SimpleEntry.class (नेस्टेड क्लास):
getName()): java.util.AbstractMap$SimpleEntry
getCanonicalName( )): java.util.AbstractMap.SimpleEntry
getSimpleName()): SimpleEntry
getTypeName(): java.util.AbstractMap$SimpleEntry

new java.io.Serializable(){}.getClass() (निनावी अंतर्गत वर्ग):
getName() ): TestReflection$1
getCanonicalName()): शून्य
getSimpleName()):
getTypeName(): TestReflection$1

आता आपल्या प्रोग्रामच्या आउटपुटचे विश्लेषण करूया:

  • getName() घटकाचे नाव परत करते.

  • getCanonicalName() जावा लँग्वेज स्पेसिफिकेशनद्वारे परिभाषित केल्याप्रमाणे, बेस क्लासचे कॅनॉनिकल नाव परत करते. बेस क्लासला कॅनॉनिकल नाव नसल्यास शून्य मिळवते (म्हणजे, जर तो स्थानिक किंवा अनामित वर्ग असेल किंवा ज्याच्या घटक प्रकाराला कॅनोनिकल नाव नसेल).

  • getSimpleName() सोर्स कोडमध्ये नमूद केल्याप्रमाणे बेस क्लासचे साधे नाव परत करते. बेस क्लास निनावी असल्यास रिकामी स्ट्रिंग मिळवते.

  • getTypeName() या प्रकारच्या नावासाठी माहितीपूर्ण स्ट्रिंग देते.