Javaで動的にnewInstanceはできるのか
◆やりたいこと
用意した値を、動的に入れ物に入れたい。
<イメージ>
◆結論
可能。
Beanなどに持たせた”フィールド”であれば可能。
変数などにはできなさそう。
◆それで作ったもの
フィールドを渡すと、それの新しいインスタンスを作るUtil。
ProjectCreateInstanceUtil.zip - Google ドライブ
また、それで作ったBeanを出力するためのVarDumpクラス。
ProjectVarDump.zip - Google ドライブ
どちらも、lib_latestフォルダにjarを置いているので、そのまま使えます。
◆経緯(もくじ)
1.まずインスタンスを作るにはどうするか?
newInstanceの使い方
2.Listの中身のクラス型(ジェネリック型/総称型)は取れるの?
総称型について解析した結果、、fieldならできるらしいぞ
3.ただし、非推奨になるらしい、、
じゃあどうするか?Constructorクラスを使う
◆経緯
1.まずインスタンスを作るにはどうするか?
Classを取得して、newInstance()を実行することで可能。
参考: Java Tips
Javaにはリフレクションという機能があり、クラスの名前やメソッド名などを変数として扱うことができます。
クラスの名前文字列からクラスのインスタンスを作成するには、ClassクラスのforNameとnewInstanceメソッドを使用します。
これによって、どのクラスを作成するかを実行時に決めることが可能になります。このような考えかたの応用にDI(Dependency Injection)があります。
Class myClass = Class.forName("MyClass");
MyClass myClassInstance = (MyClass)myClass.newInstance();
newInstance=コンストラクタの実行と考えてよい。
であれば、コンストラクタの引数にあたるものも、もちろんある。
コンストラクタBean(int)には、newInstance(int)を使えばよい。
参考: Java - newInstance()の引数 java(110863)|teratail
これで変数やフィールドに対して.classとか.getClass()とかすれば、
インスタンス生成はできるんだね!となった。
あれ?けどListは?
ListはただのListじゃなくて、StringとかBeanとかいろいろある。
この型は取得できるの??
2.Listの中身のクラス型(ジェネリック型/総称型)は取れるの?
取れる。ただし、フィールドからという制約がある。
なぜかというと、実行中のオブジェクトは、タイプ情報が消去されるから。
フィールドであれば、Fieldクラスが持っているgetGenericType()から取得できる。
参考:
・JavaオレオレFrameworkに欠かせないリフレクションでの総称型解析
・java.util.Listのジェネリック型を取得する Generics | CODE Q&A 問題解決 [日本語]
総称型を扱うには Type クラスの助けが必要になります。
Type オブジェクトは、クラスオブジェクトClass、メソッドオブジェクトMethod の引数や戻り値や例外 から取得できます。
ただ、ランタイムのオブジェクトからは総称型の情報がなくなっていて取得できません。例えば obj.getClass() で Class オブジェクトを取得してもそこから .getTypeParameter() では取得できない。
TypeVariable<Method>[] ptArray = method.getTypeParameters();
Type retType = method.getGenericReturnType();
TypeVariable<Class<X>> tvArray = clazz.getTypeParameter();
pt.getActualTypeArguments() で型パラメータの配列がとれます。List<String> の String の部分。
3.ただし、非推奨になるらしい、、
1のようにクラスでnewInstance()をすれば、インスタンス生成ができる。
リストであろうと2の方法でクラスも取得できる。
しかし、ClassのnewInstance()はJava9から非推奨になるらしい。。
参考: Class#newInstanceは非推奨になるようなので気を付けよう - 覚えたら書く
じゃあどうするか、、ConstructorのnewInstance()を使うみたい。
Class#newInstanceは使うなとなってどうすればいいかと言うと、Constructor#newInstanceによるインスタンス生成で代替すればいいようです。
(実際にはClass.getDeclaredConstructor().newInstance()を実行することになります)
けっきょくおなじことをしている…? いえ、ちがいます。
ConstructorのnewInstance()は、引数なしのものも、コンストラクタが存在しないとエラーになります。
(ClassのnewInstance()は、引数なしであれば、コンストラクタが存在しなくても実行できた。)
なので、対処はこの2点が必要。
・対象のクラスにコンストラクタを実装する
・ConstructorのnewInstance()を使う
です。
動的に何かできるなんて夢のような話です。実装しなくていいんだから!
ただ、リフレクションは必要最低限の場面で使うべきですね。速度はやはりいつも通り、問題になっています。
これに注意しつつ実装するのがいいです。
そんなかんじで。