質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.85%

  • Java

    12819questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

javaのリフレクションで、フィールドの型と名前と値を取得したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 736

nuiri1343

score 38

前提・実現したいこと

標準入力で指定したクラスのフィールドの型と名前と値を取得したいです。

以下のようなエラーがでて、フィールドの値が取得できないです。
どうしたらいいのでしょうか。

フィールドとフィールドの値を表示します.
検査するクラスを入力してください
>sample1.Point
Type : double, Name : x1, Value : java.lang.IllegalArgumentException: Can not set double field Point.x1 to java.lang.Class
Type : double, Name : y1, Value : java.lang.IllegalArgumentException: Can not set double field Point.y1 to java.lang.Class
Type : double, Name : x, Value : java.lang.IllegalArgumentException: Can not set double field Point.x to java.lang.Class
Type : double, Name : y, Value : java.lang.IllegalArgumentException: Can not set double field Point.y to java.lang.Class

該当のソースコード

リフレクションを使うクラスです。

import java.lang.reflect.Field;

public class Kensa{

    public static void main(String[] args){

    try{
        System.out.println("フィールドとフィールドの値を表示します.");
        System.out.print("検査するクラスを入力してください\n>");
        String className = new java.util.Scanner(System.in).nextLine();
        Class c = Class.forName(className);
        Field[] publicFields = c.getDeclaredFields();
        for(int i=0;i<publicFields.length;i++){
        publicFields[i].setAccessible(true);
        String fieldName = publicFields[i].getName();
        Class fieldType = publicFields[i].getType();
        String fieldTypeStr = fieldType.getName();
        System.out.print("Type : "+fieldTypeStr+", Name : "+fieldName+", Value : ");
        try{
            Object value = publicFields[i].get(c);
            System.out.println(value);
        }catch(Exception e){
            System.err.println(e);
        }

        }
    }catch(ClassNotFoundException e){
        System.err.println("クラスが見つかりません.");
        System.err.println(e);
    }
    }

}

今回、試しに検査の対象にしたクラスです。

public class Point{    
    double x1,y1,x,y;    
}

追記

申し訳ありません。
試しに検査の対象にしたクラスの例が悪かったです。

いくつか検査の対象にしたのですが、その中には引数なしのコンストラクタがないものもあります。

ので、引数なしのコンストラクタがあるクラスでも、あるクラスでも実行できるようにしたいです。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • umyu

    2018/01/01 00:13

    その場合でも、同じような判定ができると思いますが、えっと後出しで条件を追加されるのは嫌なので、こういうクラスを調査したいと質問に明記してくださいな。多分別の方が回答されるのを待ってくださいな。

    キャンセル

  • nuiri1343

    2018/01/01 00:15

    どういう場合でもと最初に書いたと思いますが、newInstanceを呼び出す際には毎回引数あるなしや、種類、数が関係してくると思ったんです。

    キャンセル

  • umyu

    2018/01/01 00:18

    最初の質問内容はこれです>標準入力で指定したクラスのフィールドの型と名前と値を取得したいです。以下のようなエラーがでて、フィールドの値が取得できないです。

    キャンセル

回答 2

checkベストアンサー

+1

こんにちは。
 

意図した動作をしないでエラーになる原因は、
 

Class c = Class.forName(className);

として取得した c を、

 Object value = publicFields[i].get(c);

と、Field#get に渡していることと思われます。

ここは c ではなく、たとえば、Pointクラスを検査対象にしているならば、
Field#get のパラメータには、Pointクラスのインスタンスを渡す必要が
あります。

これを踏まえて、ご提示のKensa.javaを以下のように修正しました。

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;

public class Kensa {

    public static void main(String[] args) {

        try {
            System.out.println("フィールドとフィールドの値を表示します.");
            System.out.print("検査するクラスを入力してください\n>");
            String className = new java.util.Scanner(System.in).nextLine();

            Class c = Class.forName(className);

            // 引数なしのコンストラクタを取得
            Constructor[] ctors = c.getDeclaredConstructors();
            Constructor ctor = null;
            for (int i = 0; i < ctors.length; i++) {
                ctor = ctors[i];
                if (ctor.getGenericParameterTypes().length == 0)
                    break;
            }

            // 引数なしのコンストラクタからインスタンスを作成
            Object obj = ctor.newInstance();

            Field[] publicFields = c.getDeclaredFields();
            for (int i = 0; i < publicFields.length; i++) {
                publicFields[i].setAccessible(true);
                String fieldName = publicFields[i].getName();
                Class fieldType = publicFields[i].getType();
                String fieldTypeStr = fieldType.getName();
                System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : ");
                try {
                    Field f = publicFields[i];
                    Object value = f.get(obj); // c ではなく obj を渡す。
                    System.out.println(value);
                } catch (Exception e) {
                    System.err.println(e);
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println("クラスが見つかりません.");
            System.err.println(e);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

同じくデフォルトパッケージにPointを作成しておいて、上記のKensaを(IntelliJで)
実行して、Pointを指定すると、以下が出力されました。

/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ ・・・以下略
フィールドとフィールドの値を表示します.
検査するクラスを入力してください
>Point
Type : double, Name : x1, Value : 0.0
Type : double, Name : y1, Value : 0.0
Type : double, Name : x, Value : 0.0
Type : double, Name : y, Value : 0.0
 
Process finished with exit code 0
 

Point クラスに引数なしのコンストラクタを明示的に書いて、
その中で、各フィールド変数の初期値を代入すれば、
それらの値が出力されるはずです。

以上、参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/30 21:54 編集

    引数なしのコンストラクタを取得する部分について、所与の Point の検査のためには、私のコードは冗長で、umyu さんのご回答にある、c.getDeclaredConstructor() でOKでした。ただし、 所与のPointとほとんど同じクラス Point2 を作り、これには明示的なコンストラクタとして、public Point2(double v) { this.x1 = v; } だけがあった場合、 この Point2 の引数ありコンストラクタを取得してインスタンスを作るには、もうひとひねり必要になるので、 Kensaを汎用的にするのは、もう少し手を加える必要がありそうです。

    キャンセル

  • 2017/12/30 22:21 編集

    どこまで質問者様が汎用性をもたせたいかによるのですが、
    引数ありのコンストラクタを呼び出したい時はc.getDeclaredConstructor(double.class).newInstance( 1.0d);が必要かと。

    キャンセル

  • 2017/12/30 22:22

    追記:コンストラクタがpublicで無いことも想定するのであれば、.setAccessible(true);も必要です><

    キャンセル

  • 2017/12/30 22:54

    > .setAccessible(true);も必要
    なるほど。コンストラクタがpublicでないときは、そのままnewInstance()すると、IllegalAccessException になるけど、そこで setAccessible(true) してやればOKになるとは知りませんでした \(^o^)/

    キャンセル

0

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Scanner;

public class Q107070 {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        try {
            Class<?> c = inputFindClass();
            // コンストラクタを呼び出してインスタンスを生成
            Object obj = c.getDeclaredConstructor().newInstance();
            for (Field fieled : c.getDeclaredFields()) {
                fieled.setAccessible(true);
                String fieldName = fieled.getName();
                Class<?> fieldType = fieled.getType();
                String fieldTypeStr = fieldType.getName();
                System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : ");
                try {
                    Object value = fieled.get(obj);
                    System.out.println(value);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    System.err.println(e);
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println("クラスが見つかりません.");
            System.err.println(e);
        }
    }

    public static Class<?> inputFindClass() throws ClassNotFoundException {
        System.out.println("フィールドとフィールドの値を表示します.");
        System.out.println("検査するクラスを入力してください");
        System.out.print(">");
        try (Scanner sc = new Scanner(System.in)) {
            String className = sc.nextLine();
            return Class.forName(className);
        }
    }
}

class Point {
    double x1, y1, x;
    double y = 2.0;// 値を取得できてるのかの確認値
}

2017/12/31追記
質問文のコメント欄の内容を受けて回答を追加。
引数の判定部分は分かりやすいようにif文にしてますが、Mapで判定するのがベターです。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Q107070 {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        try {
            Class<?> c = inputFindClass();
            Constructor<?>[] ctors = c.getDeclaredConstructors();
            Constructor<?> ctor = ctors[0];
            List<Object> params= getParams(ctor.getParameterTypes());
            // コンストラクタを呼び出してインスタンスを生成
            Object obj = ctor.newInstance(params.toArray());
            for (Field fieled : c.getDeclaredFields()) {
                fieled.setAccessible(true);
                String fieldName = fieled.getName();
                Class<?> fieldType = fieled.getType();
                String fieldTypeStr = fieldType.getName();
                System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : ");
                try {
                    Object value = fieled.get(obj);
                    System.out.println(value);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    System.err.println(e);
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println("クラスが見つかりません.");
            System.err.println(e);
        }
    }

    public static Class<?> inputFindClass() throws ClassNotFoundException {
        System.out.println("フィールドとフィールドの値を表示します.");
        System.out.println("検査するクラスを入力してください");
        System.out.print(">");
        try (Scanner sc = new Scanner(System.in)) {
            String className = sc.nextLine();
            return Class.forName(className);
        }
    }

    public static List<Object> getParams(Class<?> parameterTypes[]) throws InstantiationException,IllegalAccessException {
        List<Object>params = new ArrayList<>();
        for(Class<?> p:parameterTypes){
            if (p.isPrimitive()){
                if (p.equals(boolean.class)) {
                    params.add(false);
                    continue;
                }
                if (p.equals(byte.class)) {
                    params.add(0);
                    continue;
                }
                if (p.equals(short.class)) {
                    params.add(0);
                    continue;
                }
                if (p.equals(int.class)) {
                    params.add(0);
                    continue;
                }
                if (p.equals(char.class)) {
                    params.add('\u0000');
                    continue;
                }
                if (p.equals(float.class)) {
                    params.add(0f);
                    continue;
                }
                if (p.equals(double.class)) {
                    params.add(0d);
                    continue;
                }
            }
            params.add(p.newInstance());
        }
        return params;
    }
}

class Point {
    double x1, y1, x;
    double y = 2.0;
}
class Point2 {
    double x1=2, y1, x;
    double y = 2.0;
    public Point2(double v) { this.x1 = v; }
}
class Point3 {
    int x1, y1, x;
    int y = 2;
    public Point3(int v) { this.x1 = v; }
}
class Point4 {
    String x1="aaa", y1, x;
    int y = 2;
    public Point4(String v) { this.x1 = v; }
}
class Point5 {
    String x="aaa";
    double y = 2;
    public Point5(String x,double y) 
    { 
        this.x = x;
        this.y = y;
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.85%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    12819questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。