Почему Class.newInstance () "злой"?

Райан Делукки спросил здесь в комментарии №3 к ответу Том Хотин:

why is Class.newInstance() "evil"?

это в ответ на образец кода:

// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();

так почему это зло?

Фактически, видя ответы на этот вопрос: можно сказать это о различных применениях отражения ... не только о Class.newInstance (). Так что это действительно общее наблюдение, что «отражение побеждает проверку во время компиляции» ... что часто является точкой отражения.

Ryan Delucchi 25.10.2008 03:33

Дети в наши дни, о да, они используют слово «ЗЛО», но они никогда даже не ВИДИЛИ программу на КОБОЛ или ФОРТРАН! Вы хотите, чтобы "EVIL" взглянул на программу FORTRAN 20-летней давности, которая передавалась от проекта к проекту мастером с опытом моделирования и никаким влиянием CS! Теперь это "ЗЛО!"

NoMoreZealots 31.07.2009 05:50

См. Также stackoverflow.com/q/36272566/3888450

Stefan Dollase 29.03.2016 02:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
97
3
70 971
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Документация по Java API объясняет, почему (http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance ()):

Note that this method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.

Другими словами, он может обойти проверенную систему исключений.

Это сама природа отражения в целом ... совершенно не специфична для Constructor.newInstance ().

Ryan Delucchi 25.10.2008 03:35

@ Райан: Это неправда; все другие основанные на отражении методы вызова генерируют проверенное исключение с именем InvocationTargetException, которое обертывает любой бросаемый объект, выданный вызванным методом. Class.newInstance не будет этого делать - он вызовет проверенное исключение напрямую. Обратной стороной является то, что javac также не позволит вам попытаться перехватить эти исключения, потому что Class.newInstance не объявлен для их выдачи.

Chris Jester-Young 26.05.2011 01:32

Еще одна причина:

Современные IDE позволяют находить использование классов - это помогает во время рефакторинга, если вы и ваша IDE знаете, какой код использует класс, который вы планируете изменить.

Если вы не используете конструктор явно, а вместо этого используете Class.newInstance (), вы рискуете не обнаружить такое использование во время рефакторинга, и эта проблема не проявится при компиляции.

Также общая проблема с использованием отражения.

Ryan Delucchi 25.10.2008 03:36

Я не знаю, почему никто не предоставил простое объяснение на основе примеров по сравнению, например, с Constructor::newInstance, поскольку наконец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 обходит обработку исключений, чего вы на самом деле не хотите.

Другие вопросы по теме