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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Java

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

Q&A

解決済

2回答

4322閲覧

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

nuiri1343

総合スコア54

Java

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

0グッド

0クリップ

投稿2017/12/30 11:00

編集2017/12/30 23:34

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

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

フィールドとフィールドの値を表示します. 検査するクラスを入力してください >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

###該当のソースコード
リフレクションを使うクラスです。

java

1import java.lang.reflect.Field; 2 3public class Kensa{ 4 5 public static void main(String[] args){ 6 7 try{ 8 System.out.println("フィールドとフィールドの値を表示します."); 9 System.out.print("検査するクラスを入力してください\n>"); 10 String className = new java.util.Scanner(System.in).nextLine(); 11 Class c = Class.forName(className); 12 Field[] publicFields = c.getDeclaredFields(); 13 for(int i=0;i<publicFields.length;i++){ 14 publicFields[i].setAccessible(true); 15 String fieldName = publicFields[i].getName(); 16 Class fieldType = publicFields[i].getType(); 17 String fieldTypeStr = fieldType.getName(); 18 System.out.print("Type : "+fieldTypeStr+", Name : "+fieldName+", Value : "); 19 try{ 20 Object value = publicFields[i].get(c); 21 System.out.println(value); 22 }catch(Exception e){ 23 System.err.println(e); 24 } 25 26 } 27 }catch(ClassNotFoundException e){ 28 System.err.println("クラスが見つかりません."); 29 System.err.println(e); 30 } 31 } 32 33} 34

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

java

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umyu

2017/12/31 13:13

解決するにあたっての答えは既に全部でていますがー。例えば、引数なしのコンストラクタと引数ありのコンストラクタが同一のクラスに定義されていた場合はどのような動きが理想かを質問文に追記してくださいな。こういうクラスを検査したいと明記するほうが錯綜しないと思います。
nuiri1343

2017/12/31 13:17

引数なしとありが同時に定義されてなく、引数ありだけだった場合でも、クラスによってコンストラクタの引数の種類と数は違うので、プログラムとしてどう書くのだろう?と思いました。
nuiri1343

2017/12/31 13:57 編集

なるほど。確かに引数の種類と数がいくらかというのは調べることはできます。 しかし、例えば引数がdouble型とString型のコンストラクタだった場合と、int型だった場合で同じソースコードで実現することはできるのでしょうか。 jun68ykt様の回答のコメントでは、引数に与えているのは1.0dですが、これを汎用性あるように書く方法がわかりません。 という意味でした。
umyu

2017/12/31 14:04

えっと、答えをかくので、どうして動作するのかを考えてみてください。
umyu

2017/12/31 14:21

この回答は入力側をクラス名から変更しないという時の回答です、例えばクラス名とコンストラクタの値を入力値として渡せるのであれば、もっと違う回答になります。入力例)Point4 あいうえお など
nuiri1343

2017/12/31 14:26

getParamsの中で、プリミティブ型じゃなかった時に、p.newInstance()をしていますが、これも引数ありの場合かもしれないのではないかと思ったのですが、見落としがありますでしょうか
umyu

2017/12/31 14:28

そのp.newInstance()はプリミティブじゃないパラメータの生成です。例えばStringなど。
umyu

2017/12/31 14:29

params.add(p.newInstance());の直前の行にSystem.out.println(p);を追加してみると分かりやすいかも。
nuiri1343

2017/12/31 14:53

例えば、Point2クラスの引数にPoint3クラスがあった場合です
umyu

2017/12/31 15:13

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

2017/12/31 15:15

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

2017/12/31 15:18

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

回答2

0

Java

1import java.lang.reflect.Field; 2import java.lang.reflect.InvocationTargetException; 3import java.util.Scanner; 4 5public class Q107070 { 6 7 public static void main(String[] args) throws InstantiationException, IllegalAccessException, 8 IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { 9 try { 10 Class<?> c = inputFindClass(); 11 // コンストラクタを呼び出してインスタンスを生成 12 Object obj = c.getDeclaredConstructor().newInstance(); 13 for (Field fieled : c.getDeclaredFields()) { 14 fieled.setAccessible(true); 15 String fieldName = fieled.getName(); 16 Class<?> fieldType = fieled.getType(); 17 String fieldTypeStr = fieldType.getName(); 18 System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : "); 19 try { 20 Object value = fieled.get(obj); 21 System.out.println(value); 22 } catch (IllegalArgumentException | IllegalAccessException e) { 23 System.err.println(e); 24 } 25 } 26 } catch (ClassNotFoundException e) { 27 System.err.println("クラスが見つかりません."); 28 System.err.println(e); 29 } 30 } 31 32 public static Class<?> inputFindClass() throws ClassNotFoundException { 33 System.out.println("フィールドとフィールドの値を表示します."); 34 System.out.println("検査するクラスを入力してください"); 35 System.out.print(">"); 36 try (Scanner sc = new Scanner(System.in)) { 37 String className = sc.nextLine(); 38 return Class.forName(className); 39 } 40 } 41} 42 43class Point { 44 double x1, y1, x; 45 double y = 2.0;// 値を取得できてるのかの確認値 46}

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

Java

1import java.lang.reflect.Constructor; 2import java.lang.reflect.Field; 3import java.lang.reflect.InvocationTargetException; 4import java.util.ArrayList; 5import java.util.List; 6import java.util.Scanner; 7 8public class Q107070 { 9 10 public static void main(String[] args) throws InstantiationException, IllegalAccessException, 11 IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { 12 try { 13 Class<?> c = inputFindClass(); 14 Constructor<?>[] ctors = c.getDeclaredConstructors(); 15 Constructor<?> ctor = ctors[0]; 16 List<Object> params= getParams(ctor.getParameterTypes()); 17 // コンストラクタを呼び出してインスタンスを生成 18 Object obj = ctor.newInstance(params.toArray()); 19 for (Field fieled : c.getDeclaredFields()) { 20 fieled.setAccessible(true); 21 String fieldName = fieled.getName(); 22 Class<?> fieldType = fieled.getType(); 23 String fieldTypeStr = fieldType.getName(); 24 System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : "); 25 try { 26 Object value = fieled.get(obj); 27 System.out.println(value); 28 } catch (IllegalArgumentException | IllegalAccessException e) { 29 System.err.println(e); 30 } 31 } 32 } catch (ClassNotFoundException e) { 33 System.err.println("クラスが見つかりません."); 34 System.err.println(e); 35 } 36 } 37 38 public static Class<?> inputFindClass() throws ClassNotFoundException { 39 System.out.println("フィールドとフィールドの値を表示します."); 40 System.out.println("検査するクラスを入力してください"); 41 System.out.print(">"); 42 try (Scanner sc = new Scanner(System.in)) { 43 String className = sc.nextLine(); 44 return Class.forName(className); 45 } 46 } 47 48 public static List<Object> getParams(Class<?> parameterTypes[]) throws InstantiationException,IllegalAccessException { 49 List<Object>params = new ArrayList<>(); 50 for(Class<?> p:parameterTypes){ 51 if (p.isPrimitive()){ 52 if (p.equals(boolean.class)) { 53 params.add(false); 54 continue; 55 } 56 if (p.equals(byte.class)) { 57 params.add(0); 58 continue; 59 } 60 if (p.equals(short.class)) { 61 params.add(0); 62 continue; 63 } 64 if (p.equals(int.class)) { 65 params.add(0); 66 continue; 67 } 68 if (p.equals(char.class)) { 69 params.add('\u0000'); 70 continue; 71 } 72 if (p.equals(float.class)) { 73 params.add(0f); 74 continue; 75 } 76 if (p.equals(double.class)) { 77 params.add(0d); 78 continue; 79 } 80 } 81 params.add(p.newInstance()); 82 } 83 return params; 84 } 85} 86 87class Point { 88 double x1, y1, x; 89 double y = 2.0; 90} 91class Point2 { 92 double x1=2, y1, x; 93 double y = 2.0; 94 public Point2(double v) { this.x1 = v; } 95} 96class Point3 { 97 int x1, y1, x; 98 int y = 2; 99 public Point3(int v) { this.x1 = v; } 100} 101class Point4 { 102 String x1="aaa", y1, x; 103 int y = 2; 104 public Point4(String v) { this.x1 = v; } 105} 106class Point5 { 107 String x="aaa"; 108 double y = 2; 109 public Point5(String x,double y) 110 { 111 this.x = x; 112 this.y = y; 113 } 114}

投稿2017/12/30 12:27

編集2017/12/31 14:16
umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

こんにちは。

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

java

1Class c = Class.forName(className);

として取得した c を、

java

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

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

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

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

java

1import java.lang.reflect.Field; 2import java.lang.reflect.InvocationTargetException; 3import java.lang.reflect.Constructor; 4 5public class Kensa { 6 7 public static void main(String[] args) { 8 9 try { 10 System.out.println("フィールドとフィールドの値を表示します."); 11 System.out.print("検査するクラスを入力してください\n>"); 12 String className = new java.util.Scanner(System.in).nextLine(); 13 14 Class c = Class.forName(className); 15 16 // 引数なしのコンストラクタを取得 17 Constructor[] ctors = c.getDeclaredConstructors(); 18 Constructor ctor = null; 19 for (int i = 0; i < ctors.length; i++) { 20 ctor = ctors[i]; 21 if (ctor.getGenericParameterTypes().length == 0) 22 break; 23 } 24 25 // 引数なしのコンストラクタからインスタンスを作成 26 Object obj = ctor.newInstance(); 27 28 Field[] publicFields = c.getDeclaredFields(); 29 for (int i = 0; i < publicFields.length; i++) { 30 publicFields[i].setAccessible(true); 31 String fieldName = publicFields[i].getName(); 32 Class fieldType = publicFields[i].getType(); 33 String fieldTypeStr = fieldType.getName(); 34 System.out.print("Type : " + fieldTypeStr + ", Name : " + fieldName + ", Value : "); 35 try { 36 Field f = publicFields[i]; 37 Object value = f.get(obj); // c ではなく obj を渡す。 38 System.out.println(value); 39 } catch (Exception e) { 40 System.err.println(e); 41 } 42 } 43 } catch (ClassNotFoundException e) { 44 System.err.println("クラスが見つかりません."); 45 System.err.println(e); 46 } catch (IllegalAccessException e) { 47 e.printStackTrace(); 48 } catch (InstantiationException e) { 49 e.printStackTrace(); 50 } catch (InvocationTargetException e) { 51 e.printStackTrace(); 52 } 53 } 54} 55

同じくデフォルトパッケージに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 12:11

編集2017/12/30 12:17
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jun68ykt

2017/12/30 12:58 編集

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

2017/12/30 13:35 編集

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

2017/12/30 13:22

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

2017/12/30 13:54

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問