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

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

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

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

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

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


person Amir Arad    schedule 12.10.2008    source источник
comment
Фактически, видя ответы на этот вопрос: можно сказать это о различных применениях отражения ... не только о Class.newInstance (). Итак, это действительно общее наблюдение, что отражение побеждает проверку во время компиляции ... которая часто является точкой отражения.   -  person Ryan Delucchi    schedule 25.10.2008
comment
Дети в наши дни, о да, они используют слово ЗЛО, но они даже не ВИДИЛИ программу на КОБОЛе или Фортране! Вы хотите, чтобы EVIL взглянул на программу FORTRAN 20-летней давности, которая передавалась от проекта к проекту мастером с опытом моделирования и никаким влиянием CS! Вот это ЗЛО!   -  person NoMoreZealots    schedule 31.07.2009
comment
См. Также stackoverflow.com/q/36272566/3888450   -  person Stefan Dollase    schedule 29.03.2016


Ответы (3)


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

Обратите внимание, что этот метод распространяет любое исключение, созданное конструктором с нулевым значением, включая проверенное исключение. Использование этого метода эффективно обходит проверку исключений во время компиляции, которая в противном случае выполнялась бы компилятором. Метод Constructor.newInstance позволяет избежать этой проблемы, заключая любое исключение, созданное конструктором, в (проверено) InvocationTargetException.

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

person Chris Jester-Young    schedule 12.10.2008
comment
Такова сама природа отражения в целом ... совсем не специфична для Constructor.newInstance (). - person Ryan Delucchi; 25.10.2008
comment
@ Райан: Это неправда; все другие основанные на отражении методы вызова генерируют проверенное исключение с именем InvocationTargetException, которое обертывает любой бросаемый объект, выданный вызванным методом. Class.newInstance этого делать не будет - он вызовет проверенное исключение напрямую. Обратной стороной является то, что javac также не позволит вам попытаться перехватить эти исключения, потому что Class.newInstance не объявлено, чтобы их выбрасывать. - person Chris Jester-Young; 26.05.2011

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

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

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

person alexei.vidmich    schedule 13.10.2008
comment
Также общая проблема с использованием отражения. - person Ryan Delucchi; 25.10.2008

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

person Eugene    schedule 26.10.2018