Почему Class.newInstance () «злой»?
-
10-07-2019 - |
Вопрос
Райан Делуччи спросил здесь в комментарии № 3 к Ответ Тома Хотина :
Почему Class.newInstance () "злой"?
это в ответ на пример кода:
// 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 () ): р>
Обратите внимание, что этот метод распространяет любое исключение, выбрасываемое нулевым конструктором, включая проверенное исключение. Использование этого метода эффективно обходит проверку исключений во время компиляции, которая в противном случае выполнялась бы компилятором. Метод
Constructor.newInstance
позволяет избежать этой проблемы, поместив любое исключение, сгенерированное конструктором, в (проверенный)InvocationTargetException
.
Другими словами, он может победить систему проверенных исключений.
Другие советы
Еще одна причина:
Современные IDE позволяют вам найти использование классов - это помогает во время рефакторинга, если вы и ваша IDE знаете, какой код использует класс, который вы планируете изменить.
Если вы не используете явное использование конструктора, а используете Class.newInstance (), вы рискуете не обнаружить его при рефакторинге, и эта проблема не проявится при компиляции.
Я не знаю, почему никто не предоставил простое объяснение, основанное на этом примере, по сравнению, например, с Constructor :: newInstance
, поскольку класс finally :: 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<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
обходит обработку исключений - что вам действительно не нужно.