instanceof Vs getClass ()

114

Je constate un gain de performances lors de l'utilisation getClass()et de l' ==opérateur par rapport à l' instanceOfopérateur.

Object  str = new Integer("2000");

long starttime = System.nanoTime();

if(str instanceof String) {
    System.out.println("its string");
} else {
    if (str instanceof Integer) {
        System.out.println("its integer");

    }
}

System.out.println((System.nanoTime()-starttime));

starttime = System.nanoTime();

if(str.getClass() == String.class) {
    System.out.println("its string in equals");
} else {
    if(str.getClass() == Integer.class) {
        System.out.println("its integer");
    }
}

System.out.println((System.nanoTime()-starttime));

Existe-t-il une directive, laquelle utiliser getClass()ou instanceOf?

Étant donné un scénario: je connais les classes exactes à associer, c'est-à- Stringdire Integer(ce sont des classes finales), etc.

L'utilisation d'un instanceOfopérateur est-elle une mauvaise pratique?

goutte
la source
3
Ceci est expliqué dans: stackoverflow.com/questions/596462/… .
Clement P
2
Votre méthode de chronométrage entraîne des retards artificiels et produit des résultats de chronométrage incorrects. Échangez l'ordre dans lequel vous effectuez les vérifications et vous verrez que la première vérification que vous effectuez (== ou instanceof) sera toujours plus longue. Je suppose que ce sont les println () s. Vous ne devriez jamais inclure ces éléments dans votre bloc de chronométrage.
kurtzmarc le
Juste un commentaire à part, pour comparer les performances, utilisez plusieurs itérations de cycle (par exemple 10000) afin d'améliorer la précision. Une seule invocation n'est pas une bonne mesure.
martins.tuga

Réponses:

140

La raison pour laquelle la performance de instanceofet getClass() == ...est différente est qu'ils font des choses différentes.

  • instanceofteste si la référence d'objet sur le côté gauche (LHS) est une instance du type sur le côté droit (RHS) ou un sous-type .

  • getClass() == ... teste si les types sont identiques.

Il est donc recommandé d'ignorer le problème de performances et d'utiliser l'alternative qui vous donne la réponse dont vous avez besoin.

L'utilisation de l' instanceOfopérateur est-elle une mauvaise pratique?

Pas nécessairement. La surutilisation de l'un instanceOfou l' autre ou getClass() peut être une «odeur de conception». Si vous ne faites pas attention, vous vous retrouvez avec une conception où l'ajout de nouvelles sous-classes entraîne une quantité importante de remaniement du code. Dans la plupart des situations, l'approche préférée consiste à utiliser le polymorphisme.

Cependant, il y a des cas où ce ne sont PAS des "odeurs de design". Par exemple, dans equals(Object)vous devez tester le type réel de l'argument et renvoyer falses'il ne correspond pas. Le mieux est d'utiliser getClass().


Des termes comme «meilleure pratique», «mauvaise pratique», «odeur de conception», «anti-modèle», etc. doivent être utilisés avec parcimonie et traités avec suspicion. Ils encouragent la pensée en noir ou blanc. Il vaut mieux porter vos jugements dans leur contexte, plutôt que sur la base d'un pur dogme; par exemple, quelque chose que quelqu'un a dit est "meilleure pratique".

Stephen C
la source
@StephenC Comme vous l'avez dit, c'est code smellà utiliser soit. Cela signifie que c'est une conséquence d'un code de mauvaise conception (non polymorphe) qui vous permet d'utiliser l'un ou l'autre. puis-je déduire l'utilisation de l'un ou l'autre de cette façon?
surexchange
@overexchange - 1) J'ai dit "surutilisation" et non "utiliser". 2) A part ça, je ne comprends pas ce que vous demandez. Que voulez-vous dire par "déduire l'utilisation ..." ??? Le code utilise ces éléments ou non.
Stephen C
J'en déduis que l'utilisation de instanceof& getClass()entre en scène en raison d'une mauvaise conception existante (non polymorphe) du code. Ai-je raison?
surexchange
5
@overexchange - Vous ne pouvez pas en déduire valablement que toute utilisation de instanceof(par exemple) est une mauvaise conception. Il y a des situations où cela peut être la meilleure solution. Pareil pour getClass(). Je répète que j'ai dit «abuser» et non «utiliser» . Chaque cas doit être jugé sur ses mérites ... et non en appliquant aveuglément une règle dogmatique mal fondée.
Stephen C
44

Voulez-vous correspondre exactement à une classe , par exemple ne correspond FileInputStreamqu'à une sous-classe de FileInputStream? Si tel est le cas, utilisez getClass()et ==. Je ferais généralement cela dans un equals, afin qu'une instance de X ne soit pas considérée comme égale à une instance d'une sous-classe de X - sinon vous pouvez vous retrouver dans des problèmes de symétrie délicats. D'un autre côté, c'est plus généralement utile pour comparer que deux objets appartiennent à la même classe qu'à une classe spécifique.

Sinon, utilisez instanceof. Notez qu'avec getClass()vous devrez vous assurer que vous avez une référence non nulle pour commencer, sinon vous obtiendrez un NullPointerException, alors instanceofque vous retournerez simplement falsesi le premier opérande est nul.

Personnellement, je dirais que instanceofc'est plus idiomatique - mais utiliser l'un ou l'autre de manière intensive est une odeur de design dans la plupart des cas.

Jon Skeet
la source
18

Je sais que cela fait un moment que cela a été demandé, mais j'ai appris une alternative hier

Nous savons tous que vous pouvez faire:

if(o instanceof String) {   // etc

mais que faire si vous ne savez pas exactement quel type de classe il doit être? vous ne pouvez pas faire de manière générique:

if(o instanceof <Class variable>.getClass()) {   

car il donne une erreur de compilation.
Au lieu de cela, voici une alternative - isAssignableFrom ()

Par exemple:

public static boolean isASubClass(Class classTypeWeWant, Object objectWeHave) {

    return classTypeWeWant.isAssignableFrom(objectWeHave.getClass())
}
Andy Dingfelder
la source
8
N'utilisez pas isAssignableFrom. La bonne façon d'écrire en o instanceof Stringutilisant la réflexion est String.getClass().isInstance(o). Le javadoc le dit même: Cette méthode est l'équivalent dynamique de l' instanceofopérateur du langage Java .
Andreas
3

getClass () a la restriction que les objets ne sont égaux qu'à d'autres objets de la même classe, du même type d'exécution, comme illustré dans la sortie du code ci-dessous:

class ParentClass{
}
public class SubClass extends ParentClass{
    public static void main(String []args){
        ParentClass parentClassInstance = new ParentClass();
        SubClass subClassInstance = new SubClass();
        if(subClassInstance instanceof ParentClass){
            System.out.println("SubClass extends ParentClass. subClassInstance is instanceof ParentClass");
        }
        if(subClassInstance.getClass() != parentClassInstance.getClass()){
            System.out.println("Different getClass() return results with subClassInstance and parentClassInstance ");
        }
    }
}

Les sorties:

SubClass étend ParentClass. subClassInstance est une instance de ParentClass.

Différents getClass () renvoient des résultats avec subClassInstance et parentClassInstance.

Saurav Sahu
la source