Class.newInstance()が「悪」なのはなぜですか?
-
10-07-2019 - |
質問
Ryan Delucchi がこちらのコメント#3 トムホーティンの答え:
なぜClass.newInstance()" evil"?
コードサンプルへの応答:
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
それで、なぜそれが悪なのですか?
解決
Java APIのドキュメントでその理由が説明されています( http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance()):
このメソッドは、チェック済み例外を含め、nullaryコンストラクターによってスローされた例外を伝播することに注意してください。このメソッドを使用すると、コンパイラによって実行されるコンパイル時の例外チェックを効果的にバイパスできます。
Constructor.newInstance
メソッドは、コンストラクターによってスローされた例外を(チェック済み)InvocationTargetException
でラップすることにより、この問題を回避します。
言い換えれば、チェック例外システムを無効にすることができます。
他のヒント
もう1つの理由:
最新のIDEを使用すると、クラスの使用法を見つけることができます-あなたとあなたのIDEが、変更する予定のクラスを使用しているコードを知っている場合、リファクタリング中に役立ちます。
コンストラクターを明示的に使用せず、代わりにClass.newInstance()を使用する場合、リファクタリング中にその使用法が見つからないリスクがあり、コンパイル時にこの問題は現れません。
Constructor :: newInstance
と比較して、 finally Class :: newInstance
はjava-9以降廃止されました。
この非常に単純なクラスがあると仮定します(壊れているかどうかは関係ありません):
static class Foo {
public Foo() throws IOException {
throw new IOException();
}
}
そして、リフレクションを介してそのインスタンスを作成しようとします。最初の Class :: newInstance
:
Class<Foo> clazz = ...
try {
clazz.newInstance();
} catch (InstantiationException e) {
// handle 1
} catch (IllegalAccessException e) {
// handle 2
}
これを呼び出すと、 IOException
がスローされます-問題は、コードがそれを処理せず、 handle 1
も handle 2
でもないことです。キャッチします。
対照的に、 Constructor
を介して行う場合:
Constructor<Foo> constructor = null;
try {
constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
Foo foo = constructor.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("handle 3 called");
e.printStackTrace();
}
そのハンドル3が呼び出されるため、それを処理します。
効果的に、 Class :: newInstance
は例外処理をバイパスします-これは本当に望ましくありません。