01
mar.
2010
mar.
2010
Java Quiz #34
Java / JEE ›
Java Quiz
|
Tags :
java
Par Olivier Croisier
Vous savez qu'au sein de la JVM, une classe est identifiée de manière unique par son nom complet et son classloader.
Cette règle est facile à implémenter sous la forme d'une méthode utilitaire :
public class ClassUtils { public static boolean isSameClass(Class<?> class1, Class<?> class2) { if ((class1 == null) || (class2 == null)) { return false; } return ( class1.getClassLoader().equals(class2.getClassLoader()) && class1.getName().equals(class2.getName()) ); } }
Il ne reste plus qu'à vérifier :
System.out.println( ClassUtils.isSameClass(ClassUtils.class, ClassUtils.class) ); // true System.out.println( ClassUtils.isSameClass(String.class, ClassUtils.class) ); // false
A moins que... ?
Réponse : Ce code provoque un NullPointerException.
En effet, les classes présentes dans la bibliothèque rt.jar
sont chargées par le classloader bootstrap de la JVM, qui n'a pas de représentation sous forme de classe Java.
L'opération String.class.getClassLoader()
renvoie donc null
!
Commentaires
Perso, j'aurais plutôt fait une comparaison
class1.getClassLoader() == class2.getClassLoader()
. Si dans une sous-classe de ClassLoader, on redéfinit equals, on peut être dans une situation avec deux class loaders équivalents, mais différents.D'après la javadoc, si une classe est chargée par le Bootstrap classloader, la méthode getClassLoader peut renvoyer null, ce qui, dans ta méthode créerait une NPE. Pourquoi ne pas mettre dans la première condition :
?
isSameClass(null, null) => false ?
Bonjour à tous,
Je suis du même avis que Benoît.
Dans cet exemple, le second test se fait sur les classes String et ClassUtils qui sont de type respectif Class<String> et Class<ClassUtils>. Mais lorsque l'on souhaite récupérer le classLoader de ces deux classes, celui de String retourne null. Pourquoi ?
La javadoc de la méthode getClassLoader() dit ceci :
This method will return null in such implementations if this class was loaded by the bootstrap class loader. ... If this object represents a primitive type or void, null is returned.
Pourtant String n'est pas un type primitif mais un type objet.
Dixit ces liens wiki et javadoc, toutes les classes présentes dans le classpath sont, par défaut, chargées par le classLoader système. Or la classe String se trouve quant à elle dans le JAR <JAVA_HOME>/lib/rt.jar. Donc toujours suivant ces mêmes liens, son classLoader est bootstrap.
Donc le chargeur de la classe String est Bootstrap et la méthode String.getClassLoader() retourne null. Donc l'instruction (String.class).equals(ClassUtils.class) lève l'exception NullPointerException.
Du coup, j'ai une question, pardonnez mon ignorante : peut-t'on avoir deux références différentes pour la même classe au sein d'un même class loader ? Autrement dit, peux-t'on avoir le cas suivant :
ClassUtils.isSameClass(a, b) : true
a == b : false
Parce que sinon, je vois pas bien l'intérêt de mettre en place ce ClassUtils... Si quelqu'un veut bien éclairer ma lanterne sur comment cela peut-t'il arriver ;-)
Merci d'avance !
A Piwaï
Voici l'extrait de la JLS#12.2 qui répond à ta question :
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Well-behaved class loaders maintain these properties:
A malicious class loader could violate these properties. However, it could not undermine the security of the type system, because the Java virtual machine guards against this.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Autrement dit, la spécification Java recommande qu'un ClassLoader donné renvoie toujours la même instance de Class<?> pour désigner la même classe, mais ce comportement n'est pas absolument garanti.