web-dev-qa-db-fra.com

Pourquoi Class.newInstance () est-il "mal"?

Ryan Delucchi a demandé ici dans le commentaire # 3 à Tom Hawtin réponse:

pourquoi Class.newInstance () est-il "mal"?

ceci en réponse à l'exemple de code:

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

alors, pourquoi est-ce mal?

90
Amir Arad

La documentation de l'API Java Java explique pourquoi ( http://Java.Sun.com/javase/6/docs/api/Java/lang/Class.html#newInstance () ):

Notez que cette méthode propage toute exception levée par le constructeur nullary, y compris une exception vérifiée. L'utilisation de cette méthode contourne efficacement la vérification des exceptions au moment de la compilation qui serait sinon effectuée par le compilateur. Le Constructor.newInstance la méthode évite ce problème en encapsulant toute exception levée par le constructeur dans un (vérifié) InvocationTargetException.

En d'autres termes, il peut vaincre le système d'exceptions vérifié.

81
Chris Jester-Young

Une raison de plus:

Les IDE modernes vous permettent de trouver des utilisations de classe - cela aide pendant la refactorisation, si vous et votre IDE savez quel code utilise la classe que vous envisagez de changer.

Lorsque vous ne faites pas une utilisation explicite du constructeur, mais utilisez plutôt Class.newInstance (), vous risquez de ne pas trouver cette utilisation pendant la refactorisation et ce problème ne se manifestera pas lors de la compilation.

19
alexei.vidmich

Je ne sais pas pourquoi personne n'a fourni une explication basée sur des exemples simples par rapport à Constructor::newInstance par exemple, puisque enfinClass::newInstance est obsolète depuis Java-9.

Supposons que vous ayez cette classe très simple (peu importe qu'elle soit cassée):

static class Foo {
    public Foo() throws IOException {
        throw new IOException();
    }
}

Et vous essayez d'en créer une instance via la réflexion. Première Class::newInstance:

    Class<Foo> clazz = ...

    try {
        clazz.newInstance();
    } catch (InstantiationException e) {
        // handle 1
    } catch (IllegalAccessException e) {
        // handle 2
    }

Appeler cela entraînera un IOException étant jeté - le problème est que votre code ne le gère pas, ni handle 1 ni handle 2 l'attrapera.

En revanche, lorsque vous le faites via un 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();
    }

cette poignée 3 sera appelée, vous la manipulerez donc.

Effectivement, Class::newInstance contourne la gestion des exceptions - ce que vous ne voulez vraiment pas.

7
Eugene