Home Testo Corso Swift Type Casting

Type Casting

365
0
Type-Casting

In questa lezione parleremo della Type Casting.

La programmazione ad oggetti è quella con cui ti ritroverai a lavorare quotidianamente con le tue applicazioni.

I componenti di un’applicazione sono tutte classi o strutture che poi vengono istanziate in oggetti.

Pensa per esempio ad un bottone. Questo è generalmente composto da una forma, più o meno rettangolare e da un titolo. Ha un colore di sfondo, un font per il testo e altre varie caratteristiche.

Quindi, la programmazione ad oggetti ha riscontri che vanno oltre quello che potresti pensare.

La programmazione ad oggetti, non è un modo è il modo.

Poniamoci una domanda! Due oggetti, istanza della stessa classe, sono uguali?

 Purtroppo non puoi e non troverai mai un sistema che ti dica quando un oggetto A è uguale, o no, ad un oggetto B.

Il motivo è abbastanza semplice, se lo estendiamo alla concezione generale che ho dato, io sono un oggetto e pur essendo simile ad un altro individuo non sarò mai tale e quale a lui.

La stessa logica è stata ripresa e si ripercuote sugli oggetti di un linguaggio di programmazione come Swift.

Però, e come sempre ce n’è uno, un oggetto far parte di una famiglia, che abbiamo chiamato classe, che ne descrive le caratteristiche generali.

Questo vuol dire che, pur non potendo confrontare due oggetti tra loro, è possibile verificare se quell’oggetto è istanza di una classe oppure no oppure di trasformare un oggetto, istanza di una classe della sua gerarchia, in un altro della gerarchia delle classi a cui appartiene.

class Persona {
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
}

var a = Persona(nome: "Francesco")
var b = Persona(nome: "Mario")

a == b

Nell’esempio ho provato a fare un confronto tra 2 istanza di una classe, se provi anche tu potrei notare che il playground ti notificherà errore dicendoti che non è possibile effettuare questa operazione.

Ora ti faccio vedere un altro esempio

// Classe Nota Padre

class Nota {
    
    var nome: String
    
    init(nome: String) {
        self.nome = nome
    }
}

// Classe Testo figlia di nota

class Testo: Nota {

    var testo: String
    
    init(nome: String, testo: String?) {
        
        self.testo = testo!
        super.init(nome: nome)
    }
}

// Classe Audio figlia di nota

class Audio : Nota{
    
    var url: String?
    
    init(nome: String, url: String?) {
        
        self.url = url!
        super.init(nome: nome)
    }
}

Ho creato 3 classi una chiamata Nota una Testo figlia di nota e una chiamata Audio sempre figlia di nota. Nelle classi figlie ho inserito un super.init che mi permette di modificare il nome della nota padre.

// Creo una array che contiene le mie note
var mieNote = [    
    Testo(nome: "Nome Nota testo01", testo: "Testo Nota 01"),
    Audio(nome: "nome nota audio01", url: "indirizzo nota01"),
    Testo(nome: "Nome Nota testo02", testo: "Testo Nota 02"),
    Audio(nome: "nome nota audio02", url: "indirizzo nota02")
]

Nell’esempio ho creato un array contenete alcune istanze, 2 della classe Testo e 2 delle classe Audio.

Ora, sapresti dirmi Quale è il tipo di dato di quest’array?

Dato che gli oggetti contenuti sono di tipo Testo o Audio e dato che un array, come qualsiasi variabile, può essere descritta solo da un tipo di dato, quello che li rappresenta entrambi è il tipo Nota.

Questo è possibile grazie alla ereditarietà inversa di un oggetto.

Se sul playground inizio a scrivere la parola “mieNote” vedrò comparire il suggerimento automatico che affianca al nome della variabile mi indicata il tipo contenuto in quella variabile, che di fatto è Nota.

Quindi sono riuscito a salvare due tipi di dato diverso audio e testo in una array facendo riferimento alla classe padre proprio perché le 2 classi sono figlie dello stesso padre.

In realtà le informazioni non vengono perse perché non è avvenuta nessun tipo di conversione (a discapito di quello che si può pensare). Quello che è avvenuto è stato solo un cambio del tipo di riferimento con cui viene considerato quell’oggetto.

Cambiare il riferimento del tipo di dato con cui trattare un oggetto, prende il nome di Type Casting.

Casting: cambiamento e Type: tipo. Quindi dire che un oggetto B ha subito un type casting equivale a dire che è stato cambiato il tipo dell’oggetto B in un tipo della sua gerarchia.

Ora Per poter riconsiderare, più che convertire, un oggetto nel suo rispetto tipo di dato si utilizza la parola chiave as! che esegue il casting e ti da un feedback sul risultato della conversione.

Si parla di Down Casting perché la conversione può avvenire solamente da un tipo più in alto nella gerarchia delle classi ad un tipo più basso.

Per esempio, un oggetto di tipo Nota potrebbe contenere un oggetto Testo perché è una sua subclass. Mentre un oggetto Testo non potrebbe mai descrivere un oggetto superiore perché, per definizione, una subclass aggiunge qualcosa in più che un super o padre non ha.

Quindi scrivere nomeOggeto as! nomeClasse ti permette di riconsiderare un oggetto di un tipo A come un oggetto di un tipo B.

// ------ down Casting

var recuperoNota : Nota = Testo.init(nome: "nota01", testo: "testo nota")

// Down Casting 
var testo = recuperoNota as! Testo

Alla prima riga viene eseguito un type casting di tipo implicito ovvero: dato che la variabile a sinistra, quella a cui verrà assegnato l’oggetto recuperoNota, presenta un tipo di dato esplicito: Nota, la variabile a destra viene automaticamente convertita nel nuovo tipo di dato perché Testo fa parte della gerarchia in cui è presente anche la classe.

Alla seconda riga di codice, la var testo non presenta un tipo esplicito quindi se recuperoNota non venisse convertita nel tipo Testo, con l’operatore as!, sarebbe considerata come Nota.

Nel precedente esempio è andato tutto bene perché io conoscevo a priori la struttura delle classi.

Se questa non fosse nota il compilatore potrebbe dare errore se l’oggetto che si prova a convertire non fosse presente nella gerarchia della classe.

Ecco perché se no si e certi e meglio utilizzare la forma opzionale as? Che in questo caso se non possibile restituisce nil.

Vediamo adesso l’opzione is

l’operatore is effettua un controllo, restituendo true o false, se l’oggetto in esame è un’istanza di una determinata subclass o classe.

Sempre considerando l’esempio dell’applicazione, immagina di voler contare gli oggetti Testo e Audio presenti all’interno dell’array

var numeroTesto = 0
var numeroAudio = 0

for x in mieNote {
    
    if x is Testo{
    
        numeroTesto += 1    
    }
    else if x is Audio{
        
        numeroAudio += 1
    }
}

print("Ci sono \(numeroTesto) oggetti Testo")
print("Ci sono \(numeroAudio) oggetti Audio")

Nel codice ho effettuato un ciclo sull’array e tramite un controllo utilizzando is ho controllato che tipo di dato è, poi ho contato il loro numerico e stampo a video il risultato.

Fai attenzione perché la differenza tra as is è sostanziale:

  • as: converte il tipo di dato dell’oggetto in questione in un nuovo tipo.
  • is: verifica se un oggetto appartiene o no ad una istanza di una classe.

Questo sistema sarà molto utilizza nel prossimo corso IOS.

Per qualunque dubbio o domanda scrivi un commento in questa lezione.