Vérifier si une classe existe en Java

1. Vue d'ensemble

Vérifier l'existence d'une classe peut être utile pour déterminer quelle implémentation d'une interface utiliser. Cette technique est couramment utilisée lors des anciennes configurations JDBC.

Dans ce didacticiel, nous explorerons les nuances de l'utilisation de Class.forName () pour vérifier l'existence d'une classe dans le chemin de classe Java .

2. Utilisation de Class.forName ()

Nous pouvons vérifier l'existence d'une classe en utilisant Java Reflection, en particulier Class.forName () . La documentation montre qu'une ClassNotFoundException sera levée si la classe ne peut pas être localisée.

2.1. Quand attendre ClassNotFoundException

Tout d'abord, écrivons un test qui lancera certainement une exception ClassNotFoundException afin que nous puissions savoir que nos tests positifs sont sûrs:

@Test(expected = ClassNotFoundException.class) public void givenNonExistingClass_whenUsingForName_thenClassNotFound() throws ClassNotFoundException { Class.forName("class.that.does.not.exist"); }

Donc, nous avons prouvé qu'une classe qui n'existe pas lèvera une ClassNotFoundException . Écrivons un test pour une classe qui existe effectivement:

@Test public void givenExistingClass_whenUsingForName_thenNoException() throws ClassNotFoundException { Class.forName("java.lang.String"); }

Ces tests prouvent que l' exécution Class.forName () et ne pas attraper un ClassNotFoundException équivaut à la classe spécifiée existant sur le chemin de classe . Cependant, ce n'est pas une solution parfaite en raison des effets secondaires.

2.2. Effet secondaire: initialisation de classe

Il est essentiel de souligner que, sans spécifier de chargeur de classe, Class.forName () doit exécuter l'initialiseur statique sur la classe demandée . Cela peut entraîner un comportement inattendu.

Pour illustrer ce comportement, créons une classe qui lève une RuntimeException lorsque son bloc d'initialisation statique est exécuté afin que nous puissions savoir immédiatement quand il est exécuté:

public static class InitializingClass { static { if (true) { //enable throwing of an exception in a static initialization block throw new RuntimeException(); } } }

Nous pouvons voir dans la documentation de forName () qu'il lève une ExceptionInInitializerError si l'initialisation provoquée par cette méthode échoue.

Écrivons un test qui attendra une ExceptionInInitializerError en essayant de trouver notre InitializingClass sans spécifier de chargeur de classe:

@Test(expected = ExceptionInInitializerError.class) public void givenInitializingClass_whenUsingForName_thenInitializationError() throws ClassNotFoundException { Class.forName("path.to.InitializingClass"); }

Puisque l'exécution du bloc d'initialisation statique d'une classe est un effet secondaire invisible, nous pouvons maintenant voir comment cela pourrait causer des problèmes de performances ou même des erreurs. Voyons comment ignorer l'initialisation de la classe.

3. Dire à Class.forName () d' ignorer l' initialisation

Heureusement pour nous, il existe une méthode surchargée de forName (), qui accepte un chargeur de classe et indique si l'initialisation de la classe doit être exécutée.

Selon la documentation, les appels suivants sont équivalents:

Class.forName("Foo") Class.forName("Foo", true, this.getClass().getClassLoader())

En changeant true en false , nous pouvons maintenant écrire un test qui vérifie l'existence de notre InitializingClass sans déclencher son bloc d'initialisation statique :

@Test public void givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException() throws ClassNotFoundException { Class.forName("path.to.InitializingClass", false, getClass().getClassLoader()); }

4. Modules Java 9

Pour les projets Java 9+, il existe une troisième surcharge de Class.forName () , qui accepte un module et un nom de classe String . Cette surcharge n'exécute pas l'initialiseur de classe par défaut. En outre, notamment, il retourne null lorsque la classe demandée n'existe pas plutôt que de lancer une exception ClassNotFoundException .

5. Conclusion

Dans ce court didacticiel, nous avons exposé l'effet secondaire de l'initialisation de classe lors de l'utilisation de Class.forName () et avons constaté que vous pouvez utiliser les surcharges forName () pour éviter que cela ne se produise.

Le code source avec tous les exemples de ce didacticiel se trouve à l'adresse over sur GitHub.