C'è qualche situazione in cui non ci sono alternative a instanceof?

1

Mi sembra che instanceof provenga dalla terra della programmazione funzionale ed è una versione annacquata di pattern matching e che l'OO ad altenativo dovrebbe essere di tipo dynamic dispatching.

Nelle lingue OO (pensa Ruby), c'è qualche situazione in cui instanceof è l'unica soluzione? Non riesco a pensare a me stesso, specialmente in Ruby, dove puoi applicare patch a qualsiasi classe con qualsiasi metodo che ti piaccia.

    
posta adrianton3 04.05.2013 - 10:02
fonte

4 risposte

5

Il solito argomento per evitare instanceof è "Invece di controllare il tipo di una cosa per decidere un'azione, dovresti fare di quella azione un metodo per quella cosa, e usare l'ereditarietà per assicurarti che ogni cosa faccia automaticamente il giusto cosa." In altre parole, usa il meccanismo di ricerca dei metodi integrato nel linguaggio piuttosto che programmare il tuo.

Tuttavia, di solito è limitato a single dispatch : selezionare un'azione in base al tipo esatto di one partecipante. Se ci sono due o più partecipanti con tipi diversi, semplicemente non funziona, perché la maggior parte delle lingue ha solo un dispatch integrato. A volte puoi riorganizzare le cose in modo che i due partecipanti diventino argomenti a un metodo su un terzo e usa il metodo sovraccarico, che supporta la selezione in base a diversi tipi di parametri, ma (di nuovo, di solito) solo quando le alternative non sono sottotipi l'una dell'altra. Può anche essere risolto in fase di compilazione piuttosto che in fase di esecuzione, che cambia in modo sottile la semantica del tuo programma.

Questa non è affatto l'unica cosa che instanceof rende possibile, ma a mio avviso la più rilevante.

    
risposta data 04.05.2013 - 10:15
fonte
1

Ci sono situazioni in cui instanceof è inevitabile. Questo vale per le lingue, dove una classe può essere chiusa. Ad esempio, in Java ci possono essere classi finali. Non puoi ereditare da loro, né puoi fare in modo che una tale classe implementi un'interfaccia tua.

Un esempio potrebbe essere quello di avere una lista contenente oggetti Integer e Double e si desidera fare cose diverse in base al tipo.

    
risposta data 04.05.2013 - 13:53
fonte
0

Non proprio, no. In un linguaggio che supporta le interfacce (esplicitamente o sotto forma di ereditarietà multipla con classi virtuali pure, come C ++), puoi sempre utilizzare il seguente modello:

interface Fooable {
    void foo();
}

abstract class BaseBaz {
    public abstract Fooable asFooable();
}

class BazOne extends BaseBaz {
    public Fooable asFooable() { return null; } // not a Fooable!
}

class BazTwo extends BaseBaz implements Fooable {
    public void foo() { Console.print("I am teh foobar!"); }
    public Fooable asFooable() { return this; }
}

// --- snip ---

void runFooOnBaz(Baz baz) {
    Fooable fooable = baz.asFooable();
    if (fooable == null)
        throw new NotFooableException();
    fooable.foo();
}

Cioè, ti avvicini ad ogni oggetto tramite l'interfaccia, e dai alla classe base un metodo per restituirti l'oggetto tramite l'interfaccia, se possibile. (Probabilmente esiste un nome per questo pattern, ma non lo so, se qualcuno lo sa, sentiti libero di modificarlo.)

In un linguaggio di dattilografia come Python, dovresti seguire una strada diversa: piuttosto che chiedere se l'oggetto eredita da una determinata classe, devi solo fingere di farlo e prepararti per il fallimento:

class BazOne(object):
    pass

class BazTwo(object):
    def foo():
        print "I am teh foobar!"

def runFoo(baz):
    try:
        baz.foo()
    except AttributeError:
        raise NotFooableError()

Tuttavia, quest'ultimo non è sempre possibile; motivi per cui non includere:

  • Si può fare affidamento su più di un metodo e la prima chiamata ha effetti collaterali che non è possibile consentire a meno che non esista anche il secondo metodo. Ispezionare l'oggetto anziché eseguirlo nell'eccezione in genere può risolverlo, ma non è necessariamente molto carino.
  • Potrebbe essere necessario decidere come trattare l'oggetto in un modo che non dipende da un particolare metodo; ad esempio, potresti voler trattare unicode e string oggetti in modo diverso.
  • Gli oggetti potrebbero implementare un metodo con lo stesso nome, ma con significati diversi - hero.save(hostage) e file.save(filename) sono cose completamente indipendenti, ma i nomi dei metodi si scontrano, quindi basta controllare se esiste un metodo chiamato save può dare falsi positivi.

E poi c'è la situazione in cui devi decidere un percorso di codice basato su più di un oggetto; puoi senz'altro lavorare usando l'approccio del metodo auto-descrittivo, o testando gli attributi rilevanti di tutti gli oggetti, ma a volte, instanceof è solo più facile da scrivere, più leggibile e più conciso, in altre parole, il pragmatico scelta.

    
risposta data 04.05.2013 - 10:28
fonte
0

Sebbene l'instanceof debba essere evitata, alcune interfacce Java lo richiedono come parte del loro contratto.

Ad esempio: javax.security.auth.callback.CallbackHandler.

Definisce:

void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException

Il callback è un'interfaccia senza metodi, quindi l'unico modo per differenziare i diversi tipi di oggetti Callback è l'uso di instanceof. La documentazione di CallbackHandler.handle () incoraggia l'uso di instanceof.

    
risposta data 28.07.2015 - 08:55
fonte

Leggi altre domande sui tag