覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Class#newInstanceは非推奨になるようなので気を付けよう

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でコード上に無い例外をスローしてみる

SampleObjectClass#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#newInstanceConstructor#newInstanceの例外に関する動きの違いを確認しました。
Class.newInstance()を使っている箇所は、早めにClass.getDeclaredConstructor().newInstance() に置き換えましょう。



関連エントリ