CodeGym /Java Course /モジュール 2: Java コア /リフレクションを使用したデータの取得

リフレクションを使用したデータの取得

モジュール 2: Java コア
レベル 17 , レッスン 1
使用可能

java.lang.reflect.Field クラス

Fieldクラスは、クラスまたはインターフェイスの単一フィールドに関する情報と、そのフィールドへの動的アクセスを提供しますField では、get または set アクセス操作中に拡大型変換も可能ですが、縮小が発生した場合はIllegalArgumentExceptionがスローされます。

Fieldオブジェクトを取得するには、まずクラスを作成します。


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

これは、クラスのフィールドのリストを取得する方法であり、後で使用します。結果は次のとおりです。

[private java.lang.String com.company.person.name、private int com.company.person.age、public boolean com.company.Person.is Male、protected java.lang.String com.company.Person.address、public静的最終整数com.company.Person.MAX_AGE、パブリック静的最終整数com.company.Person.MIN_AGE]

では、このデータを使って何ができるかを考えてみましょう。Fieldクラスのメソッドについて話しましょう。

方法 説明
getType() このオブジェクトによって表されるフィールドの宣言された型を識別する Class オブジェクトを返します。分野物体。
getAnnotatedType() を返します注釈付きタイプこの Field によって表されるフィールドの宣言された型を指定するための型の使用を表すオブジェクト。
getGenericType() を返しますタイプこれによって表されるフィールドの宣言された型を表すオブジェクト分野物体。
getName() これで表されるフィールドの名前を返します。分野物体。
getModifiers() これで表されるフィールドの Java 言語修飾子をエンコードした int を返します。分野物体。
getAnnotations() このフィールドの注釈を返します。注釈がない場合は、空の配列を返します。

getType()、getName()、および getModifiers() メソッド

getType()メソッドを使用してフィールドのタイプを取得できます。メソッドを書いてみましょう:


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

そして、次の結果が得られます。

クラス java.lang.String
int
ブール値
クラス java.lang.String
int
int

次に、フィールドの名前を取得するメソッドをクラスに追加しましょう。これにより、クラスのフィールド間の移動が容易になります。


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

結果がよりわかりやすくなりました。

フィールドの種類 - class java.lang.String
フィールド名 - name

フィールドの種類 - int
フィールド名 - age

フィールドの種類 - boolean
フィールド名 - is Male フィールドの

種類 - class java.lang.String
フィールド名 - address

フィールドの種類 - int
フィールド名 - MAX_AGE

フィールドタイプ - int
フィールド名 - MIN_AGE

素晴らしい!メソッドをもう少し修正してみましょう。ここにアクセス修飾子を追加します


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

e.getModifiers() が何を返すかを見てみましょう。このメソッドは、フィールドのアクセス修飾子の状態を判断できるintを返します。Modifierクラスには、フィールドの特定の各修飾子を担当する静的変数が含まれています

戻り値をModifier.toString()でラップし、その値をテキストとしてすぐに取得しましょう。

フィールド タイプ - class java.lang.String
フィールド名 - name
修飾子 - private

フィールド タイプ - int
フィールド名 - age
修飾子 - private

フィールド タイプ - boolean
フィールド名 - is Male
修飾子 - public

フィールド タイプ - class java.lang.String
フィールド名 - address
修飾子 - protected

フィールドの種類 - int
フィールド名 - MAX_AGE
修飾子 - public static Final

フィールドの種類 - int
フィールド名 - MIN_AGE
修飾子 - public staticfinal

Modifier.toString()を使用しない場合は次のようになります。

フィールド タイプ - class java.lang.String
フィールド名 - name
修飾子 - 2

フィールド タイプ - int
フィールド名 - age
修飾子 - 2

フィールド タイプ - boolean
フィールド名 - is Male
修飾子 - 1

フィールド タイプ - class java.lang.String
フィールド名 - address
修飾子 - 4

フィールド タイプ - int
フィールド名 - MAX_AGE
修飾子 - 25

フィールド タイプ - int
フィールド名 - MIN_AGE
修飾子 - 25

getAnnotations()、getAnnotatedType()、および getGenericType() メソッド

これらのメソッドを使用できるようにPersonクラスを変更しましょう。独自のアノテーションを作成し、それをフィールドに適用します。さらにいくつかのフィールドを追加します。

2 つのアノテーションを作成しましょう。Pig Latin の変数名を 1 つに渡し、2 つ目を要素に使用します。


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

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

そして、メインクラスとPersonクラスを変更します。


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

メソッドの結果を見て、その目的を理解しましょう。

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

getGenericType:
java.lang.Class<java.lang.オブジェクト>
java.util.List<java.lang.String>
クラス java.lang.String
クラス [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 は、指定されたフィールドの注釈を返します (存在する場合)。フィールドの注釈を取得し、それを完全に確認できます。

  • getGenericType を使用すると、パラメーター化された型を正しく表示できます。

  • getAnnotations は、オブジェクトに適用されたアノテーションを返します。

これにより、クラス内の各フィールドに関するすべてのデータだけでなく、そのアクセス修飾子、注釈、データ型も簡単に取得できるようになります。

java.lang.reflect.Method クラス

素晴らしい!私たちはクラスの分野について話しました。今度はメソッドについて話します。

Methodオブジェクトを取得するには、 getMethodメソッドを呼び出し、メソッドの名前を渡します。これはMethodオブジェクトを取得する基本的な方法です。


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

これからもクラス全員で協力していきます。getter と setter、および hashCode、 equalstoStringメソッドを追加しましょう。


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

次に、 Methodクラスを使用して調べる一連のメソッドを準備しましょう。最も重要なメソッドのリストを次に示します。

方法 説明
getName() メソッドの名前を返します。
getModifiers() このメソッドのアクセス修飾子を返します。
getReturnType() メソッドの戻り値の型を返します。
getGenericReturnType() ジェネリックメソッドを考慮して、メソッドの戻り値の型を返します。
getParameterTypes() メソッドパラメータの配列を返します。
getGenericParameterTypes() ジェネリックメソッドを考慮したメソッドパラメータの配列を返します。
getExceptionTypes() メソッドがスローできる例外を返します。
getGenericExceptionTypes() パラメータ化された型を考慮して、メソッドがスローできる例外を返します。
getAnnotations() 親アノテーションを含むメソッドのアノテーションを返します。
getDeclaredAnnotations() 親アノテーションを除く、メソッドのアノテーションを返します。

クラスのメソッドの配列を取得するには、次のメソッドを呼び出します。


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

これにより、クラス内のすべてのメソッドが得られます。

getName() メソッドと getModifiers() メソッド

getName を使用して各メソッドの名前を取得できます。


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

次に、修飾子を取得するために、 getModifiersを使用するメソッドを作成しましょう。


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

主な方法は次のとおりです。


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

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

私たちの結果:

[getName, 等しい, toString, hashCode, setName, getAddress, is Male, getAge, setAge, set Male, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

すべてのメソッドにはpublic修飾子があるため、最後のメソッドは 1 の配列を返します。コードを変更すると、修飾子自体が表示されます。


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

    System.out.println(getMethodsName(methods));
    System.out.println(modifyModifiers(getModifiers(methods)));
}
[getName, 等しい, toString, hashCode, setName, getAddress, is Male, getAge, setAge, set Male, setAddress] [パブリック, パブリック, パブリック, パブリック, パブリック, パブリック, パブリック, パブリック, パブリック, パブリック, パブリック
]

getReturnedType()

このメソッドを使用すると、メソッドの戻り値の型を取得できます。


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

getGenericReturnType()

パラメータ化された型でラップされた型を返すメソッドをPersonクラスに与え、その戻り値を取得してみます。


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

そして、メインメソッドを更新します。


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

私たちの方法の結果:

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() メソッドと getGenericParameterTypes() メソッド

引き続きPersonクラスを変更し、さらに 2 つのメソッドを追加します。


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

1 つ目ではメソッドのパラメータを取得でき、2 つ目ではパラメータ化された型も取得できます。


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

いずれかのメソッドのみにアクセスします。特定の名前でメソッドにアクセスするには、getMethodを呼び出し、必要なメソッドの名前とパラメーターを渡します。


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

コードを実行すると、メソッドがどのように異なり、何を返すかがわかります。

インタフェース java.util.List
クラス java.lang.String

java.util.List<java.lang.String>
クラス java.lang.String

getExceptionTypes() メソッドと getGenericExceptionTypes() メソッド

これらのメソッドを使用すると、メソッドがスローできる例外の配列と、パラメーター化された型を持つ例外 (存在する場合) を取得できます。非表示の静的クラスを持つ新しい例を使用してみましょう。


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

    private void process() throws IOException {}
}

そして、 Processorクラスのメソッドを呼び出します


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

これで、例外が確認できます。

[クラスjava.io.IOException]

次に、型をパラメータ化しましょう。メインクラスを変更します。


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

そして、メインメソッドのコードは次のとおりです


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

このメソッド内で、型変数の汎用親インターフェイスであるTypeVariablesオブジェクトを取得しました。そしてその中で、内部パラメータ、つまりネストされた例外を取得できるようになりました。

[E]
クラス java.io.IOException

getAnnotations() メソッドと getDeclaredAnnotations() メソッド

この新しいクラスを引き続き使用し、いくつかの注釈を追加しましょう。独自のAnnotationアノテーションを作成します。


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

そしてそれを私たちのメソッドに適用します。


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

}

そしてもちろん、すべての注釈を表示するメソッドを追加します。


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

mainメソッドの実装:


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

結果の画面出力は次のようになります。

プロセス
[@com.company.Main&Annotation(key="key", value="value")]

これは、メソッドに適用されたアノテーションを取得する方法であり、getAnnotationsメソッドを使用すると、クラスの親アノテーションにもアクセスできます。

今日は、リフレクションがメソッドやフィールドでどのように機能するのか、そしてリフレクションを使用してどのようなデータを取得できるのかについて学びました。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION