Réflexion de tableau Java: isArray vs instanceof

177

Y a-t-il une différence de préférence ou de comportement entre l'utilisation de:

if(obj.getClass().isArray()) {}

et

if(obj instanceof Object[]) {}

?

David Citron
la source

Réponses:

203

Dans la plupart des cas, vous devez utiliser le instanceof opérateur pour tester si un objet est un tableau.

En règle générale, vous testez le type d'un objet avant le downcasting vers un type particulier qui est connu au moment de la compilation. Par exemple, vous avez peut-être écrit du code qui peut fonctionner avec un Integer[]ou un int[]. Vous voudriez garder vos lancers avec instanceof:

if (obj instanceof Integer[]) {
    Integer[] array = (Integer[]) obj;
    /* Use the boxed array */
} else if (obj instanceof int[]) {
    int[] array = (int[]) obj;
    /* Use the primitive array */
} else ...

Au niveau JVM, l' instanceofopérateur se traduit par une "instanceof" spécifique octet de code , optimisé dans la plupart des implémentations JVM.

Dans des cas plus rares, vous pouvez utiliser la réflexion pour parcourir un graphe d'objets de types inconnus. Dans des cas comme celui-ci, leisArray() méthode peut être utile car vous ne connaissez pas le type de composant au moment de la compilation; vous pourriez, par exemple, implémenter une sorte de mécanisme de sérialisation et être capable de passer chaque composant du tableau à la même méthode de sérialisation, quel que soit son type.

Il existe deux cas particuliers: les références nulles et les références aux tableaux primitifs.

Une référence nulle entraînera instanceofle résultat false, tandis que le isArrayjette unNullPointerException .

Appliqué à un tableau primitif, le instanceofrendement donne à falsemoins que le type de composant sur l'opérande de droite corresponde exactement au type de composant. En revanche, isArray()retournera truepour tout type de composant.

Erickson
la source
7
Ouais, mais quelle différence cela va-t-il faire? Cela me semble être une micro-optimisation.
Michael Myers
2
@Dims Si vous affirmez que cela obj instanceof int[]cède falselorsque vous attribuez un int[]à obj, vous vous trompez.
erickson
4
Désolé, je voulais dire que obj instanceof Object[]cède falsesi Object obj = new int[7].
Dim
3
D'accord, en Java, les types de données primitifs ne sont pas des objets et ne s'étendent pas java.lang.Object, donc cela a du sens. Mais instanceofpeut toujours être utilisé pour tester les tableaux primitifs.
erickson
4
-1: En général, puisque les tableaux primitifs sont des tableaux appelants, isArray()il faut utiliser. Dans le cas particulier très peu général de n'avoir que des tableaux d'objets, instanceoffournit une alternative de haute performance.
Sam Harwell
33

Dans ce dernier cas, si obj est nul, vous n'obtiendrez pas une NullPointerException mais une false.

Burkhard
la source
8

Si objest de type, int[]disons, alors cela aura un tableau Classmais pas une instance de Object[]. Alors, que voulez-vous faire obj. Si vous voulez le lancer, allez-y instanceof. Si vous comptez utiliser la réflexion, utilisez .getClass().isArray().

Tom Hawtin - Tacle
la source
4

getClass().isArray() est nettement plus lent sur Sun Java 5 ou 6 JRE que sur IBM.

À tel point que l'utilisation clazz.getName().charAt(0) == '['est plus rapide sur Sun JVM.

Sébastien Tardif
la source
10
Avez-vous des statistiques, une étude de cas ou d'autres preuves avec lesquelles vous pouvez établir un lien?
David Citron
4

J'ai récemment rencontré un problème lors de la mise à niveau d'une application Groovy du JDK 5 au JDK 6. L'utilisation a isArray()échoué dans JDK6:

MissingMethodException:
No signature of sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl.isArray() ...

Changer pour instanceof Object[]résoudre ce problème.

dturanski
la source
Cela n'a aucun sens. isArrayest une méthode de Class, non Type, donc bien sûr GenericArrayTypeImpln'a pas cette méthode. Et getClassvous ne pouvez jamais retourner un non- Class Type, donc vous (ou Groovy ??) devez avoir fait quelque chose de mal pour obtenir cela, comme en supposant que tout Typeest un Class.
kaqqao
3

La réflexion de tableau Java est pour les cas où vous n'avez pas d'instance de la classe disponible pour faire "instanceof". Par exemple, si vous écrivez une sorte de framework d'injection, qui injecte des valeurs dans une nouvelle instance d'une classe, comme le fait JPA, vous devez utiliser la fonctionnalité isArray ().

J'ai blogué à ce sujet plus tôt en décembre. http://blog.adamsbros.org/2010/12/08/java-array-reflection/

Trenton D. Adams
la source
2

Si jamais vous avez le choix entre une solution réfléchissante et une solution non réfléchissante, ne choisissez jamais la solution réfléchissante (impliquant des objets de classe). Ce n'est pas que ce soit «faux» ou quoi que ce soit, mais tout ce qui implique une réflexion est généralement moins évident et moins clair.

Bill K
la source
Euh, eh bien, "instanceof" est une sorte de réflexion (ou, du moins, d'introspection) aussi, mais je comprends votre point.
David Citron
En outre, il est généralement plus lent.
Mad Physicist
0

Il n'y a aucune différence de comportement que je puisse trouver entre les deux (autre que le cas nul évident). Quant à la version à préférer, j'irais avec la seconde. C'est la manière standard de faire cela en Java.

Si cela déroute les lecteurs de votre code (parce que String[] instanceof Object[]c'est vrai), vous pouvez utiliser le premier pour être plus explicite si les réviseurs de code continuent de vous poser des questions à ce sujet.

hazzen
la source