Operatore instanceof in Java

Concetti Chiave
  • L'operatore instanceof consente di verificare il tipo di un oggetto a tempo di esecuzione.
  • Può essere utilizzato per evitare errori di cast non validi.
  • La forma tradizionale di instanceof è stata migliorata con il pattern matching a partire da JDK 17.
  • L'operatore instanceof restituisce true se l'oggetto è del tipo specificato o può essere sottoposto a cast in quel tipo, altrimenti restituisce false.

Introduzione a instanceof

A volte, conoscere il tipo di un oggetto durante l'esecuzione di un programma risulta utile.

Per esempio, si potrebbe avere un thread di esecuzione che genera vari tipi di oggetti, e un altro thread che elabora questi oggetti. In questa situazione, potrebbe essere utile per il thread di elaborazione conoscere il tipo di ogni oggetto quando lo riceve.

Un'altra situazione in cui la conoscenza del tipo di un oggetto a tempo di esecuzione è importante riguarda il casting. In Java, un cast non valido causa un errore a tempo di esecuzione. Molti cast non validi possono essere rilevati al momento della compilazione. Tuttavia, i cast che coinvolgono gerarchie di classi possono produrre cast non validi che possono essere rilevati solo a tempo di esecuzione. Per esempio, una super-classe chiamata A può produrre due sottoclassi, chiamate B e C. Quindi, fare il cast di un oggetto B nel tipo A o fare il cast di un oggetto C nel tipo A è legale, ma fare il cast di un oggetto B nel tipo C (o viceversa) non è legale.

Poiché un oggetto di tipo A può riferirsi a oggetti di B o C, come si può conoscere, a tempo di esecuzione, che tipo di oggetto viene effettivamente riferito prima di tentare il cast al tipo C? Potrebbe essere un oggetto di tipo A, B o C. Se è un oggetto di tipo B, verrà lanciata un'eccezione a tempo di esecuzione.

Java fornisce l'operatore a tempo di esecuzione instanceof per rispondere a questa domanda.

Prima di iniziare, è necessario affermare che instanceof è stato significativamente migliorato da JDK 17 con una potente nuova funzionalità basata sul pattern matching. Qui, viene presentata la forma tradizionale di instanceof. Studieremo il pattern matching nella prossima lezione.

L'operatore instanceof tradizionale ha questa forma generale:

riferimento_obj instanceof tipo

Qui, riferimento_obj è un riferimento a un'istanza di una classe, e tipo è un tipo di classe. Se riferimento_obj è del tipo specificato o può essere sottoposto a cast nel tipo specificato, allora l'operatore instanceof restituisce true. Altrimenti, il suo risultato è false. Quindi, instanceof è il mezzo attraverso il quale un programma può ottenere informazioni sul tipo a tempo di esecuzione di un oggetto.

Analizziamo il programma seguente che dimostra l'uso di instanceof:

// Esempio di applicazione dell'operatore instanceof in Java

// Classe base A
class A {
    int i, j;
}

// Classe B
class B {
    int i, j;
}

// Classe C che deriva da A
class C extends A {
    int k;
}

// Classe D che deriva da A
class D extends A {
    int k;
}

class InstanceOf {

    public static void main(String[] args) {

        // Crea istanze delle classi
        A a = new A();
        B b = new B();
        C c = new C();
        D d = new D();

        // a è certamente un'istanza di A
        if (a instanceof A)
            System.out.println("a è un'istanza di A");

        // b è certamente un'istanza di B
        if (b instanceof B)
            System.out.println("b è un'istanza di B");

        // c è un'istanza di C, ma anche di A
        if (c instanceof C)
            System.out.println("c è un'istanza di C");
        if (c instanceof A)
            System.out.println("c può essere convertito in A");

        // a non è un'istanza di C quindi non può essere convertito in C
        if (a instanceof C)
            System.out.println("a può essere convertito in C");

        System.out.println();

        // confronta i tipi dei tipi derivati
        A ob;
        ob = d; // Un riferimento a d
        System.out.println("ob ora fa riferimento a d");

        if (ob instanceof D)
            System.out.println("ob è un'istanza di D");
        System.out.println();

        ob = c; // Un riferimento a c
        System.out.println("ob ora fa riferimento a c");
        if (ob instanceof D)
            System.out.println("ob può essere convertito in D");
        else
            System.out.println("ob non può essere convertito in D");
        if (ob instanceof A)
            System.out.println("ob può essere convertito in A");
        System.out.println();

        // tutti gli oggetti possono essere sottoposti a cast a Object
        if (a instanceof Object)
            System.out.println("a può essere convertito in Object");
        if (b instanceof Object)
            System.out.println("b può essere convertito in Object");
        if (c instanceof Object)
            System.out.println("c può essere convertito in Object");
        if (d instanceof Object)
            System.out.println("d può essere convertito in Object");
    }

}

L'output di questo programma è mostrato qui:

a è un'istanza di A
b è un'istanza di B
c è un'istanza di C
c può essere convertito in A

ob ora fa riferimento a d
ob è un'istanza di D

ob ora fa riferimento a c
ob non può essere convertito in D
ob può essere convertito in A

a può essere convertito in Object
b può essere convertito in Object
c può essere convertito in Object
d può essere convertito in Object

L'operatore instanceof non è necessario per la maggior parte dei programmi semplici, perché, spesso, si conosce in anticipo il tipo di oggetto con cui si sta lavorando.

Tuttavia, può essere molto utile quando si scrivono routine generalizzate che operano su oggetti di una gerarchia di classi complessa o che sono creati da codice al di fuori del proprio controllo diretto. Come vedremo, i miglioramenti del pattern matching descritti nella prossima lezione ne semplificano l'uso.