JavaRush /Kurslar /All lectures for UZ purposes /Ma'lumotlarni refleksiya orqali olish

Ma'lumotlarni refleksiya orqali olish

All lectures for UZ purposes
Daraja, Dars
Mavjud

java.lang.reflect.Field klassi

Field klassi o'z ichida klass yoki interfeysning bitta maydoni haqida ma'lumot va dinamik kirishni taqdim etadi. Field ham kengaytirilgan o'zgarishlarga ruxsat beradi, lekin IllegalArgumentException istisnosini chiqaradi, agar o'zgarish toraytirilsa.

Filed klassini olish uchun, biz foydalanmoqchi bo'lgan klassni va uning ishlov beruvchisini yozamiz:


public class Person {
    private String name;
    private int age;
    
    public boolean isMale;
    
    protected String address;
    
    public static final int MAX_AGE = 120;
    public static final int MIN_AGE = 0;
}

Va ishlov beruvchining o'zi:


public class Main {
    public static void main(String[] args) {
        Field[] fields = Person.class.getDeclaredFields();
        List<Field> actualFields = getFieldNames(fields);
        System.out.println(actualFields);
    }

    static List<Field> getFieldNames(Field[] fields) {
        return List.of(fields);
    }
}

Shunday qilib, biz klassimizning maydonlar ro'yxatini olamiz, bu bilan kelajakda ishlaymiz. Natija quyidagicha ko'rinadi:

[private java.lang.String com.company.Person.name, private int com.company.Person.age, public boolean com.company.Person.isMale, protected java.lang.String com.company.Person.address, public static final int com.company.Person.MAX_AGE, public static final int com.company.Person.MIN_AGE]

Endi biz ushbu ma'lumotlar to'plami bilan nima qilishi mumkinligimizni ko'rib chiqamiz. Field klassining metodlari haqida gaplashamiz:

Metod Tavsif
getType() Bu obyekt Field tomonidan ifodalangan deklaratsiyalangan maydonning tipini aniqlovchi klass obyektini qaytaradi.
getAnnotatedType() Bu maydon tomonidan ifodalangan maydonning deklaratsiyalangan tipini belgilash uchun foydalaniladigan tipni ifodalaydigan AnnotatedType obyektini qaytaradi.
getGenericType() Bu obyekt Field tomonidan ifodalangan deklaratsiyalangan maydonning tipini ifodalaydigan Type obyektini qaytaradi.
getName() Bu obyekt Field tomonidan ifodalangan maydon nomini qaytaradi.
getModifiers() Bu obyekt Field tomonidan ifodalangan maydon uchun Java tilidagi modifikatorlarni butun son sifatida qaytaradi.
getAnnotations() Bu maydonning annotatsiyalarini qaytaradi. Agar annotatsiyalar yo'q bo'lsa - bo'sh massiv.

getType(), getName(), getModifiers() metodlari

getType() metodi yordamida biz o'zimizning maydonimizning turini olishimiz mumkin. Metod yozamiz:


static void printTypes(List<Field> fields){
      fields.forEach(e -> System.out.println(e.getType()));
  }

Va natijani quyidagicha olamiz:

class java.lang.String
int
boolean
class java.lang.String
int
int

Va darhol klassimizda maydon nomini olish uchun metod qo'shamiz. Shunda biz klassimiz maydonlariga ko'proq yo'naltirilgan bo'lamiz.


static void printTypesAndNames(List<Field> fields){
   fields.forEach(e -> System.out.printf("Field type - %s\nField name - %s\n\n", e.getType(), e.getName()));
}

Foydalanuvchi uchun tushunarliroq bo'lgan natijani olamiz:

Field type - class java.lang.String
Field name - name

Field type - int
Field name - age

Field type - boolean
Field name - isMale

Field type - class java.lang.String
Field name - address

Field type - int
Field name - MAX_AGE

Field type - int
Field name - MIN_AGE

Ajoyib, keling, metodimizni yana modifikatsiya qilamiz! Bu yerga kirish modifikatorlarini qo'shamiz:


static void printFieldInfo(List<Field> fields){
   fields.forEach(e -> System.out.printf("Field type - %s\nField name - %s\nModifiers - %s\n\n", e.getType(), e.getName(), Modifier.toString(e.getModifiers())));
}

Va e.getModifiers() nima qaytarishini tahlil qilamiz. Bu metod modifikatorlarni aniqlash uchun ichida o'z ichiga oladigan butun int sonni qaytaradi. Modifier klassida maydonning ma'lum modifikatori uchun javob beradigan statik o'zgaruvchilar mavjud.

Bizning qaytariladigan qiymatimizni Мodifier.toString() bilan o'rab olamiz — va darhol matnli ko'rinishda qiymatni olamiz:

Field type - class java.lang.String
Field name - name
Modifiers - private

Field type - int
Field name - age
Modifiers - private

Field type - boolean
Field name - isMale
Modifiers - public

Field type - class java.lang.String
Field name - address
Modifiers - protected

Field type - int
Field name - MAX_AGE
Modifiers - public static final

Field type - int
Field name - MIN_AGE
Modifiers - public static final

Va bu qanday ko'rinishga ega Мodifier.toString() holda:

Field type - class java.lang.String
Field name - name
Modifiers - 2

Field type - int
Field name - age
Modifiers - 2

Field type - boolean
Field name - isMale
Modifiers - 1

Field type - class java.lang.String
Field name - address
Modifiers - 4

Field type - int
Field name - MAX_AGE
Modifiers - 25

Field type - int
Field name - MIN_AGE
Modifiers - 25

getAnnotations(), getAnnotatedType(), getGenericType() metodlari

Dasturimizni mavjud metodlar bilan ishlash uchun modifikatsiya qilaylik. Biz o'z annotationimizni yozamiz, uni maydonlarimizga qo'shamiz va yana bir nechta maydon qo'shamiz.

Ikki annotation yaratamiz. Biriga o'zgaruvchining nomini rus tilida uzatamiz, ikkinchi annotation elementlar uchun ishlatiladi:


@Target(value=ElementType.FIELD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Name {
    String name();
}

@Target({ ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Number {
}

Asosiy klassimiz va Person klassini o'zgartiramiz:


public class Person {
    @Name(name = "Имя")
    private String name;

    @Name(name = "Никнэймы пользователя")
    List<String> nicknames;

    private final Class<Object> type;

    private int @Number[] number;

    public Person(Class<Object> type) {
        this.type = type;
    }
}

public static void main(String[] args) {
    Field[] fields = Person.class.getDeclaredFields();
    List<Field> actualFields = getFieldNames(fields);
    
    printAdditionalInfo(actualFields);
}

static void printAdditionalInfo(List<Field> fields) {
   System.out.println("\ngetAnnotatedType:");
   fields.forEach(e -> System.out.println(e.getAnnotatedType()));

   System.out.println("\ngetGenericType:");
   fields.forEach(e -> System.out.println(e.getGenericType()));

   System.out.println("\ngetAnnotations:");
   fields.forEach(e -> System.out.println(Arrays.toString(e.getAnnotations())));
}

Metod natijalarini tekshirish va ularning maqsadini yana bir bor ko'rib chiqish vaqti keldi:

getAnnotatedType:
java.lang.Class<java.lang.Object>
java.util.List<java.lang.String>
java.lang.String
int @Number()[]

getGenericType:
java.lang.Class<java.lang.Object>
java.util.List<java.lang.String>
class java.lang.String
class [I

getAnnotations:
[]
[@Name(name="\u041d\u0438\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f")]
[@Name(name="\u0418\u043c\u044f")]
[]
  • getAnnotatedType agar mavjud bo'lsa, ushbu maydon uchun annotationni qaytaradi. Bizda maydon uchun annotation bor va biz uni ko'rib turibmiz.

  • getGenericType jeneralizatsiyalangan parametrlarni to'g'ri ko'rsatadi.

  • getAnnotations obyekt ustida turgan annotationlarni qaytaradi.

Shunday qilib, biz klassimizdagi har bir maydon haqida, uning kirish modifikatorlari, annotationlar va ma'lumotlar turlari haqida barcha ma'lumotlarni osonlik bilan olamiz.

java.lang.reflect.Method klassi

Zo'r, biz klassimizning maydonlari haqida gaplashdik, endi metodlar haqida gaplashadigan vaqt keldi.

Method klassi obyektini olish uchun biz getMethod chaqiramiz va metod nomini unga uzatamiz. Bu Method klassi olish uchun asosiy usul:


Method getNameMethod =  Person.class.getMethod("getName");

Dasturimiz bilan ishlashni davom ettiramiz. Getter va setterlar, hash code, equals va toString qo'shamiz:


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

    public boolean isMale;

    protected String address;

    public static final int MAX_AGE = 120;
    public static final int MIN_AGE = 0;

    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;
    }

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && isMale == person.isMale && Objects.equals(name, person.name) && Objects.equals(address, person.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, isMale, address);
    }
}

Endi, Method klassida ko'rmoqchi bo'lgan metodlar to'plamini tayyorlaymiz. Mana asosiy metodlar ro'yxati:

Metod Tavsif
getName() Metod nomini qaytaradi.
getModifiers() Ushbu metodning kirish modifikatorini qaytaradi.
getReturnType() Metodning qaytariladigan turini qaytaradi.
getGenericReturnType() Jeneralizatsiyalangan metodlarni hisobga olgan holda metodning qaytariladigan turini qaytaradi.
getParameterTypes() Metod parametrlarining massivini qaytaradi.
getGenericParameterTypes() Jeneralizatsiyalangan metodlarni hisobga olgan holda metod parametrlarining massivini qaytaradi.
getExceptionTypes() Metod chiqarishi mumkin bo'lgan istisnolarni qaytaradi.
getGenericExceptionTypes() Jeneralizatsiyalangan parametrlarni hisobga olgan holda metod chiqarishi mumkin bo'lgan istisnolarni qaytaradi.
getAnnotations() Metod uchun, shu jumladan ota-annotationlarni, annotationlar qaytaradi.
getDeclaredAnnotations() Metod uchun, ota-annotationlarni hisobga olmasdan, annotationlar qaytaradi.

Klassimiz qaytaradigan metodlar massivini olish uchun biz quyidagi metodni chaqiramiz:


Method[] methods = Person.class.getDeclaredMethods();

Bu yordamida biz klassimizdagi barcha metodlarni olamiz.

getName() va getModifiers() metodlari

Barcha metodlarning nomlarini olish uchun biz getName dan foydalanishimiz mumkin:


static List<String> getMethodsName(Method[] methods) {
    return Arrays.asList(methods)
            .stream()
            .map(Method::getName)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}

Va modifikatorlarni olish uchun, getModifiers dan foydalanib metod yozamiz:


static List<String> getModifiers(Method[] methods) {
    return Arrays
            .stream(methods)
            .map(Method::getModifiers)
            .map(String::valueOf)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}

Bizning main metodimiz:


public static void main(String[] args) {
    Method[] methods = Person.class.getDeclaredMethods();

    System.out.println(getMethodsName(methods));
    System.out.println(getModifiers(methods));
}

Bizning natijamiz:

[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Barcha modifikatorlarimiz public kirishga ega, shuning uchun oxirgi metod bizga birliklarni qaytaradi. Agar biz kodimizni modifikatsiya qilsak, modifikatorlarimizni ko'ramiz:


public static void main(String[] args) {
    Method[] methods = Person.class.getDeclaredMethods();

    System.out.println(getMethodsName(methods));
    System.out.println(modifyModifiers(getModifiers(methods)));
}
[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[public, public, public, public, public, public, public, public, public, public, public]

getReturnedType()

Bu metod yordamida biz metodning qaytariladigan turini olishimiz mumkin:


static void getReturnedType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getReturnType)
            .forEach(System.out::println);
}
class java.lang.String
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void

getGenericReturnType()

Person klassimizga jeneralizatsiyalangan turda qaytaradigan metod qo'shamiz va uning qaytariladigan qiymatini olishga harakat qilamiz:


public List<String> someMethod() {
    //juda foydali va muhim metod
    return null;
}

Va asosiy metodimizni modifikatsiya qilamiz:


static void getGenericReturnType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getGenericReturnType)
            .forEach(System.out::println);
}

Metod natijasi:

class java.lang.String
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void
java.util.List<java.lang.String>

getParameterTypes() va getGenericParameterTypes() metodlari

Person klassimizdagi metodimizni modifikatsiya qilishda davom etamiz va yana ikki metod qo'shamiz:


public List<String> someMethod(List<String> list, String s) {
    //juda foydali va muhim metod
    return null;
}

Birinchisi metodlarimizning parametrlarini olishimizga, ikkinchisi esa jeneralizatsiyalangan parametrlarni ham chiqarishga imkon beradi.


static void getParameterTypes(Method[] methods) {
    Class<?>[] types = method.getParameterTypes();
        for (Class<?> type : types) {
            System.out.println(type);
        }
}

static void getGenericParameterTypes(Method[] methods) {
   Type[] types = method.getGenericParameterTypes();
        for (Type type : types) {
            System.out.println(type);
        }
}

Faqatgina bitta metodimizga murojaat qilamiz. Ma'lum bir nom bo'yicha metodga murojaat qilish uchun, getMethod chaqiramiz va unga kerakli metod nomi va parametrlarini uzatamiz:


public static void main(String[] args) throws NoSuchMethodException {
    Method currentMethod = Person.class.getMethod("someMethod", List.class, String.class);

    getParameterTypes(currentMethod);
    System.out.println();
    getGenericParameterTypes(currentMethod);
}

Kodimiz natijalariga ko'ra, metodlar qanday farq qilishini va ular nima qaytarishini ko'ramiz:

interface java.util.List
class java.lang.String

java.util.List<java.lang.String>
class java.lang.String

getExceptionTypes() va getGenericExceptionTypes() metodlari

Bu metodlar yordamida biz metodimiz chiqarishi mumkin bo'lgan istisnolar massivini va agar mavjud bo'lsa, jeneralizatsiyalangan istisnolarni olishimiz mumkin. Sirli statik klass bilan yangi misolni ko'rib chiqamiz:


private static class Processor {
    private void init() {}

    private void process() throws IOException {}
}

Processor klassimizda metodlarni chaqiramiz:


public static void main(String... args) throws NoSuchMethodException {
    Method method = Processor.class.getDeclaredMethod("process");
    Type[] type = method.getExceptionTypes();
    System.out.println(Arrays.toString(type));
}

Natijada bizning istisnomiz aniq ko'rinib turibdi:

[class java.io.IOException]

Endi barchasini jeneralikka o'rab olamiz. Asosiy klassimizni modifikatsiya qilamiz:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Va Main klassining kodi:


public static void main(String... args) throws NoSuchMethodException {
    Method m = Processor.class.getDeclaredMethod("process");
    Type[] t = m.getGenericExceptionTypes();
    System.out.println(Arrays.toString(t));

    for (Type type : t) {
        if (type instanceof TypeVariable) {
            for (Type type1 : ((TypeVariable) type).getBounds()) {
                System.out.println(type1);
            }
        }
    }
}

Bu metod ichida biz TypeVariables oldik — bu tur o'zgaruvchilari uchun umumiy super interfeys. Ichida esa biz ichki parametrni, ya'ni ichki istisnomizni olishimiz mumkin:

[E]
class java.io.IOException

getAnnotations() va getDeclaredAnnotations() metodlari

Yangi klassimiz bilan ishlashni davom etamiz va unga bir nechta annotation qo'shamiz. Annotation nomli o'z annotationimizni yaratamiz:


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

    public String key();
    public String value();
}

Uni metodimizga qo'shamiz:


@Annotation(key = "key", value = "value")
private void process() throws E{

}

Va barcha annotationlarimizni ko'rsatish uchun metod, albatta:


static void getMethodAnnotations(Class<?> clazz) {
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println(method.getName());
        System.out.println(Arrays.toString(method.getAnnotations()));
        System.out.println();
    }
}

Bizning Main klassimizning amalga oshirilishi:


public static void main(String... args) {
    Class clazz = Processor.class;
    getMethodAnnotations(clazz);
}

Ekranda quyidagi natijani olamiz:

process
[@com.company.Main&Annotation(key=”key”, value=”value”)]

Shu tariqa biz metodlarimizga tegishli annotationlarni olamiz, va getAnnotations metodi yordamida biz klassning ota-annotationlariga ham kirish imkoniga ega bo'lamiz.

Bugun sen bilan biz refleksiya yordamida metod va maydonlarning qanday ishlashini va u orqali qanday ma'lumotlar olishimiz mumkinligini bilib oldik!

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