Javaでリフレクションによるデフォルトコンストラクタ呼び出しでインスタンス生成する方法としてClass#newInstance
が挙げられると思います。
ただし、Class#newInstance
はJava9で非推奨(Deprecated)になるようです。(さらに以降のバージョンでAPI自体が削除になるんですかね・・・)
Class#newInstance
は、シグニチャ上に無いチェック例外がスローできてしまうため、それが問題で非推奨となったようです。
その辺の動きについて今回のエントリで確認してみます。
インスタンス化対象のクラス
今回は以下クラスのインスタンス化を行って動きを確認します。
このクラスは、デフォルトコンストラクタを実行するとFileNotFoundException
(チェック例外)をスローするようになっています。
import java.io.FileNotFoundException; public class SampleObject { public SampleObject() throws FileNotFoundException { throw new FileNotFoundException("target file not found."); } }
Class#newInstanceでコード上に無い例外をスローしてみる
SampleObject
をClass#newInstance
でインスタンス化するコードを書いて実行してみます。
■実行用のサンプルコード
public class ClazzNewInstanceTrial { public static void main(String[] args) { try { SampleObject obj = createSampleObject(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } private static SampleObject createSampleObject() throws IllegalAccessException, InstantiationException { return SampleObject.class.newInstance(); } }
createSampleObject
メソッド内でClass#newInstance
を実行してSampleObject
のインスタンスを生成していますが、
createSampleObject
メソッドのシグニチャにはスローする例外としてIllegalAccessException
, InstantiationException
のみが登場しており、
FileNotFoundException
は登場しません。
これを実行してみると以下の結果が出ます
■実行結果
Exception in thread "main" java.io.FileNotFoundException: target file not found. at sample.app.reflection.SampleObject.<init>(SampleObject.java:8) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at java.lang.Class.newInstance(Class.java:442) at sample.app.reflection.ClazzNewInstanceTrial.createSampleObject(ClazzNewInstanceTrial.java:16) at sample.app.reflection.ClazzNewInstanceTrial.main(ClazzNewInstanceTrial.java:7)
なんとまぁ、メソッドのシグニチャに登場していなかったのに、createSampleObject
メソッドがFileNotFoundException
をスローしています。
コード上に現れていないチェック例外がスローできてしまっています。
代替案の Constructor#newInstanceの場合
Class#newInstance
は使うなとなってどうすればいいかと言うと、Constructor#newInstance
によるインスタンス生成で代替すればいいようです。
(実際にはClass.getDeclaredConstructor().newInstance()
を実行することになります)
SampleObject
のインスタンス化をしてみてClass#newInstance
との例外に関する動きの違いを確認してみます
■実行用のサンプルコード
import java.lang.reflect.InvocationTargetException; public class ConstructorNewInstance { public static void main(String[] args) { try { SampleObject obj = createSampleObject(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private static SampleObject createSampleObject() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { return SampleObject.class.getDeclaredConstructor().newInstance(); } }
これを実行してみると以下の結果が出ます
■実行結果
java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at sample.app.reflection.ConstructorNewInstance.createSampleObject(ConstructorNewInstance.java:23) at sample.app.reflection.ConstructorNewInstance.main(ConstructorNewInstance.java:9) Caused by: java.io.FileNotFoundException: target file not found. at sample.app.reflection.SampleObject.<init>(SampleObject.java:8) ... 6 more
createSampleObject
メソッドからは、InvocationTargetException
でラップされたFileNotFoundException
がスローされて、それをcatchできています。
メソッドのシグニチャ通りの例外がスローされています。
蛇足
Java7から、java.lang.ReflectiveOperationException
がリフレクション関係の例外の親クラスとして定義されてますので
上記の実行サンプルのコードはReflectiveOperationException
を使ってもう少しシンプル記述できます。
■実行用のサンプルコード
public class ConstructorNewInstance2 { public static void main(String[] args) { try { SampleObject obj = createSampleObject(); } catch (ReflectiveOperationException e) { e.printStackTrace(); } } private static SampleObject createSampleObject() throws ReflectiveOperationException { return SampleObject.class.getDeclaredConstructor().newInstance(); } }
■実行結果
java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at sample.app.reflection.ConstructorNewInstance2.createSampleObject(ConstructorNewInstance2.java:14) at sample.app.reflection.ConstructorNewInstance2.main(ConstructorNewInstance2.java:7) Caused by: java.io.FileNotFoundException: target file not found. at sample.app.reflection.SampleObject.<init>(SampleObject.java:8) ... 6 more
InvocationTargetException
でラップされたFileNotFoundException
がスローされて、それをReflectiveOperationException
でcatchできています
まとめ
Class#newInstance
とConstructor#newInstance
の例外に関する動きの違いを確認しました。
Class.newInstance()
を使っている箇所は、早めにClass.getDeclaredConstructor().newInstance()
に置き換えましょう。