CodeGym /Java Kursu /Modül 2: Java Çekirdeği /Yansıma kullanarak veri alma

Yansıma kullanarak veri alma

Modül 2: Java Çekirdeği
Seviye , Ders
Mevcut

java.lang.reflect.Field sınıfı

Field sınıfı, bir sınıf veya arayüzün tek bir alanı hakkında bilgi ve bu alana dinamik erişim sağlar . Field ayrıca bir erişim alma veya ayarlama işlemi sırasında genişleyen bir tür dönüşümüne izin verir, ancak daralma meydana gelirse bir IllegalArgumentException atar.

Bir Field nesnesi elde etmek için önce bir sınıf yazacağız:


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

Ve işte o sınıfla çalışmak için kodumuz:


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

Daha sonra çalışacağımız sınıfımızın alanlarının listesini bu şekilde alıyoruz. İşte sonuç:

[özel java.lang.String com.company.Person.name, özel int com.company.Person.age, genel boolean com.company.Person.isMale, korumalı java.lang.String com.company.Person.address, genel statik nihai int com.company.Person.MAX_AGE, genel statik nihai int com.company.Person.MIN_AGE]

Şimdi bu verilerle neler yapabileceğimize bakalım. Field sınıfının metotlarından bahsedelim :

Yöntem Tanım
getType() Bununla temsil edilen alanın belirtilen türünü tanımlayan bir Class nesnesi döndürür.Alannesne.
getAnnotatedType() bir döndürürAçıklamalıTürBu Alan tarafından temsil edilen alanın belirtilen türünü belirtmek için bir türün kullanımını temsil eden nesne.
getGenericType() bir döndürürTipbununla temsil edilen alanın belirtilen türünü temsil eden nesneAlannesne.
getName() Bununla temsil edilen alanın adını döndürürAlannesne.
getModifiers() Bununla temsil edilen alan için Java dili değiştiricilerini kodlayan bir int döndürür.Alannesne.
getAnnotations() Bu alan için açıklamaları döndürür. Ek açıklama yoksa, boş bir dizi döndürür.

getType(), getName() ve getModifiers() yöntemleri

Alanımızın türünü elde etmek için getType() yöntemini kullanabiliriz . Bir metod yazalım:


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

Ve şu sonucu elde ederiz:

sınıf java.lang.String
int
boole
sınıfı java.lang.String
int
int

Şimdi bir alanın adını almak için sınıfımıza bir metot ekleyelim. Bu, sınıfımızın alanlarında gezinmeyi kolaylaştıracaktır.


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

Şimdi sonuç daha anlaşılır:

Alan türü - sınıf java.lang.String
Alan adı - ad

Alan türü - int
Alan adı - age

Alan türü - boolean
Alan adı - isMale

Alan türü - class java.lang.String
Alan adı - adres

Alan türü - int
Alan adı - MAX_AGE

Alan türü - int
Alan adı - MIN_AGE

Harika! Yöntemimizi biraz daha değiştirelim! Buraya erişim değiştiricileri ekleyeceğiz


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

Ve e.getModifiers()' ın ne döndürdüğüne bakalım . Bu yöntem , alanımızın erişim değiştiricilerinin durumunu belirlememizi sağlayan bir int döndürür . Değiştirici sınıfı , alanın her belirli değiştiricisinden sorumlu statik değişkenler içerir.

Dönüş değerimizi Modifier.toString() içine saralım ve değerini hemen metin olarak alalım:

Alan türü - sınıf java.lang.String
Alan adı - ad
Değiştiriciler - özel

Alan türü - int
Alan adı - yaş
Değiştiriciler - özel

Alan türü - boolean
Alan adı - isMale
Değiştiriciler - public

Alan türü - sınıf java.lang.String
Alan adı - adres
Değiştiriciler - korumalı

Alan tipi - int
Alan adı - MAX_AGE
Değiştiriciler - public static final

Alan tipi - int
Alan adı - MIN_AGE
Değiştiriciler - public static final

İşte Modifier.toString() olmadan nasıl göründüğü :

Alan türü - sınıf java.lang.String
Alan adı - ad
Değiştiriciler - 2

Alan türü - int
Alan adı - yaş
Değiştiriciler - 2

Alan türü - boolean
Alan adı - isMale
Değiştiriciler - 1

Alan türü - sınıf java.lang.String
Alan adı - adres
Değiştiriciler - 4

Alan tipi - int
Alan adı - MAX_AGE
Değiştiriciler - 25

Alan tipi - int
Alan adı - MIN_AGE
Değiştiriciler - 25

getAnnotations(), getAnnotatedType() ve getGenericType() yöntemleri

Bu yöntemlerle çalışmak için Person sınıfını değiştirelim . Kendi ek açıklamamızı yazıp alanlarımıza uygulayacağız. Ve birkaç alan daha ekleyeceğiz.

İki ek açıklama oluşturalım. Pig Latince'deki değişken ismini one'a aktaracağız ve ikincisini elementler için kullanacağız:


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

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

Ve ana sınıfımızı ve Person sınıfını değiştireceğiz :


public class Person {
    @Name(name = "Ame-nay")
    private String name;

    @Name(name = "User nicknames")
    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())));
}

Yöntemlerimizin sonuçlarına bakmanın ve bunların ne işe yaradığını anlamanın zamanı geldi:

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

getGenericType:
java.lang.Class<java.lang. Nesne>
java.util.List<java.lang.String>
sınıfı java.lang.String
sınıfı [I

getAnnotations:
[]
[@Name(name="\u0055\u0073\u0065\u0072\u0020\u006e\u0069\u0063) \u006b\u006e\u0061\u006d\u0065\u0073")]
[@Name(name="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [
]
  • getAnnotatedType, varsa, verilen alan için açıklamayı döndürür. Alan için ek açıklamayı alıyoruz ve onu mükemmel bir şekilde görebiliyoruz.

  • getGenericType, parametreleştirilmiş bir türü doğru şekilde görüntülemenizi sağlar.

  • getAnnotations, nesnemize uygulanan açıklamaları döndürür.

Bu, sınıfımızdaki her alanla ilgili tüm verileri, erişim değiştiricilerini, ek açıklamalarını ve veri türlerini bu şekilde kolayca alabiliriz.

java.lang.reflect.Method sınıfı

Süper! Sınıfımızın alanlarından bahsettik. Şimdi yöntemler hakkında konuşma zamanı.

Bir Method nesnesi elde etmek için getMethod yöntemini çağırıp ona yöntemimizin adını veririz. Bu, bir Method nesnesi elde etmenin temel yoludur :


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

Sınıfımızla çalışmaya devam edeceğiz. Alıcıları ve ayarlayıcıları ve hashCode, equals ve toString yöntemlerini ekleyelim :


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

Şimdi Method sınıfını kullanarak inceleyeceğimiz bir dizi metot hazırlayalım . İşte en önemli yöntemlerin bir listesi:

Yöntem Tanım
getName() Yöntemin adını döndürür.
getModifiers() Bu yöntemin erişim değiştiricisini döndürür.
getReturnType() Yöntemin dönüş türünü döndürür.
getGenericReturnType() Genel yöntemleri hesaba katarak yöntemin dönüş türünü döndürür.
getParameterTypes() Bir dizi yöntem parametresi döndürür.
getGenericParameterTypes() Genel yöntemleri açıklayan bir dizi yöntem parametresi döndürür.
getExceptionTypes() Yöntemin oluşturabileceği istisnaları döndürür.
getGenericExceptionTypes() Parametreli türleri hesaba katarak yöntemin atabileceği istisnaları döndürür.
getAnnotations() Üst notlar da dahil olmak üzere, yöntem için notları döndürür.
getDeclaredAnnotations() Üst notlar hariç, yöntem için notları döndürür.

Will sınıfımızın metotlarının bir dizisini elde etmek için şu metodu çağırabiliriz:


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

Bize sınıfımızdaki tüm yöntemleri verecektir.

getName() ve getModifiers() yöntemleri

Her yöntemin adını almak için getName'i kullanabiliriz :


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

Şimdi değiştiricileri almak için getModifiers kullanan bir yöntem yazalım :


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

İşte ana yöntemimiz:


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

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

Bizim sonucumuz:

[getName, eşittir, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Tüm yöntemlerimiz genel değiştiriciye sahiptir, bu nedenle son yöntem bir dizi döndürür. Kodumuzu değiştirirsek, değiştiricilerimizin kendilerini görürüz:


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

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

getReturnedType()

Bu yöntem, yöntemin dönüş türünü almamızı sağlar:


static void getReturnedType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getReturnType)
            .forEach(System.out::println);
}
sınıf java.lang.String
boole
sınıfı java.lang.String
int
geçersiz
sınıf java.lang.String
boolean
int
geçersiz
geçersiz
geçersiz

getGenericReturnType()

Person sınıfımıza, parametreleştirilmiş bir türe sarılmış türü döndüren bir yöntem verelim ve dönüş değerini almaya çalışalım:


public List<String> someMethod() {
    // Very useful and important method
    return null;
}

Ve ana yöntemimizi güncelleyeceğiz:


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

Yöntemimizin sonucu:

sınıf java.lang.String
boole
sınıfı java.lang.String
int
geçersiz
sınıf java.lang.String
boolean
int
geçersiz
geçersiz
geçersiz
java.util.List<java.lang.String>

getParameterTypes() ve getGenericParameterTypes() yöntemleri

İki yöntem daha ekleyerek Person sınıfımızı değiştirmeye devam ediyoruz :


public List<String> someMethod(List<String> list, String s) {
    // Very useful and important method
    return null;
}

İlki, metotlarımızın parametrelerini almamıza izin verecek ve ikincisi de bize parametreleştirilmiş tipler verecek.


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

Yöntemlerimizden yalnızca birine erişeceğiz. Belirli bir ada sahip bir yönteme erişmek için getMethod'u çağırır ve istediğimiz yöntemin adını ve parametrelerini iletiriz:


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

Kodumuzu çalıştırarak, yöntemlerin nasıl farklılaştığını ve ne döndürdüğünü göreceğiz:

arayüz Java.util.List
sınıfı

Java.lang.String Java.util.List<java.lang.String>
sınıfı Java.lang.String

getExceptionTypes() ve getGenericExceptionTypes() yöntemleri

Bu yöntemleri, yöntemimizin atabileceği bir dizi istisna ve ayrıca (varsa) parametreleştirilmiş türlerle istisnalar elde etmek için kullanabiliriz. Gizli statik sınıfı olan yeni bir örnek kullanalım:


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

    private void process() throws IOException {}
}

Ve İşlemci sınıfımızdaki yöntemleri çağıracağız :


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

Şimdi istisnamızı görebiliriz:

[sınıf java.io.IOException]

Şimdi türü parametrelendirelim. Ana sınıfımızı değiştireceğiz:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Ve ana yöntemin kodu :


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 yöntemin içinde, tür değişkenleri için genel bir üst arabirim olan TypeVariables nesnesine sahibiz . Ve bunun içinde artık dahili parametreyi, yani yuvalanmış istisnamızı alabiliriz:

[E]
sınıfı java.io.IOException

getAnnotations() ve getDeclaredAnnotations() yöntemleri

Bu yeni sınıfı kullanmaya devam edelim ve ona birkaç açıklama ekleyelim. Kendi Ek Açıklama ek açıklamamızı oluşturacağız :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Ve bunu yöntemimize uygulayın:


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

}

Ve elbette tüm ek açıklamalarımızı görüntülemek için bir yöntem ekleyeceğiz:


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

Ana yöntemimizin uygulanması :


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

Ortaya çıkan ekran çıktısı:

işlem
[@com.company.Main&Annotation(key="key", value="value")]

Metotlarımıza uygulanan ek açıklamaları bu şekilde alabiliriz ve getAnnotations yöntemi, sınıfın üst açıklamalarına da erişmemizi sağlar.

Bugün, yansımanın yöntemler ve alanlarla nasıl çalışabileceğini ve onunla hangi verileri elde edebileceğimizi öğrendik!

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