Kotlin From Scratch classi astratte, interfacce, ereditarietà e alias di tipo

Kotlin è un linguaggio di programmazione moderno che viene compilato in bytecode Java. È gratuito e open source e promette di rendere la codifica per Android ancora più divertente.  

Nell'articolo precedente, hai imparato di più sulle proprietà di Kotlin come l'inizializzazione tardiva, l'estensione e le proprietà in linea. Non solo, hai anche imparato a conoscere classi avanzate come dati, enum, classi nidificate e sigillate in Kotlin. 

In questo post, continuerai a conoscere la programmazione orientata agli oggetti in Kotlin imparando a conoscere classi, interfacce ed eredità astratte. Per un bonus, imparerai anche sugli alias dei tipi. 

1. Classi astratte

Kotlin supporta classi astratte, proprio come Java, sono classi da cui non si intende mai creare oggetti. Una classe astratta è incompleta o inutile senza alcune sottoclassi concrete (non astratte), dalle quali è possibile creare un'istanza di oggetti. Una sottoclasse concreta di una classe astratta implementa tutti i metodi e le proprietà definiti nella classe astratta, altrimenti quella sottoclasse è anche una classe astratta!

Creiamo una classe astratta con il astratto modificatore (simile a Java). 

abstract class Employee (val firstName: String, val lastName: String) abstract fun earnings (): Double

Nota che non tutti i membri devono essere astratti. In altre parole, possiamo avere un'implementazione predefinita del metodo in una classe astratta. 

abstract class Employee (val firstName: String, val lastName: String) // ... fun fullName (): String return lastName + "" + firstName; 

Qui abbiamo creato la funzione non astratta nome e cognome() in una classe astratta Dipendente. Le classi concrete (sottoclassi della classe astratta) possono sovrascrivere l'implementazione predefinita di un metodo astratto, ma solo se il metodo ha il Aperto modificatore specificato (imparerai di più su questo a breve). 

Possiamo anche memorizzare lo stato in classi astratte. 

abstract class Employee (val firstName: String, val lastName: String) // ... val propFoo: String = "bla bla"

Anche se la classe astratta non definisce alcun metodo, dobbiamo creare una sottoclasse prima di poterla istanziare, come nell'esempio seguente.

class Programmer (firstName: String, lastName: String): Employee (firstName, lastName) override fun earnings (): Double // calcola guadagni

Nostro Programmatore la classe estende il Dipendente classe astratta. In Kotlin usiamo un singolo carattere del colon (:) invece di Java si estende parola chiave per estendere una classe o implementare un'interfaccia. 

Possiamo quindi creare un oggetto di tipo Programmatore e chiama i metodi su di esso - sia nella sua classe che nella superclasse (classe base).  

val programmer = Programmer ("Chike", "Mgbemena") println (programmer.fullName ()) // "Mgbemena Chike"

Una cosa che potrebbe sorprendervi è che abbiamo la possibilità di ignorare a val (immutabile) proprietà con var (mutevole). 

open class BaseA (open val baseProp: String)  class DerivedA: BaseA ("") private var derivedProp: String = "" override var baseProp: String get () = derivedProp set (valore) derivedProp = valore

Assicurati di utilizzare questa funzionalità con saggezza! Tieni presente che non possiamo eseguire l'inversione di tendenza a var immobili con val

2. Interfacce

Un'interfaccia è semplicemente una raccolta di metodi correlati che in genere ti consentono di dire agli oggetti cosa fare e anche come farlo di default. (I metodi predefiniti nelle interfacce sono una nuova funzionalità aggiunta a Java 8.) In altre parole, un'interfaccia è un contratto che le classi di implementazione devono rispettare. 

Un'interfaccia è definita usando il interfaccia parola chiave in Kotlin (simile a Java). 

classe Classe risultato Interfaccia studente StudentRepository fun getById (id: Long): Student fun getResultsById (id: Long): List 

Nel codice sopra, abbiamo dichiarato a StudentRepository interfaccia. Questa interfaccia contiene due metodi astratti: getById () e getResultsById (). Si noti che incluso il astratto la parola chiave è ridondante in un metodo di interfaccia perché sono già astrattamente implicitamente. 

Un'interfaccia è inutile senza uno o più implementatori, quindi creiamo una classe che implementerà questa interfaccia. 

class StudentLocalDataSource: StudentRepository override fun getResults (id: Long): List // do implementation sostituisce fun getById (id: Long): Student // do implementation

Qui abbiamo creato una classe StudentLocalDataSource che implementa il StudentRepository interfaccia.

Noi usiamo il oltrepassare modificatore per etichettare i metodi e le proprietà che vogliamo ridefinire dall'interfaccia o dalla superclasse - questo è simile al @Oltrepassare annotazione in Java.

Nota le seguenti regole aggiuntive di interfacce in Kotlin:

  • Una classe può implementare tutte le interfacce che vuoi, ma può estendere solo una singola classe (simile a Java).
  • Il oltrepassare il modificatore è obbligatorio in Kotlin, diversamente da Java. 
  • Insieme ai metodi, possiamo anche dichiarare le proprietà in un'interfaccia Kotlin. 
  • Un metodo di interfaccia Kotlin può avere un'implementazione predefinita (simile a Java 8). 

Vediamo un esempio di un metodo di interfaccia con un'implementazione predefinita.

interface StudentRepository // ... fun delete (studente: studente) // do implementation

Nel codice precedente, abbiamo aggiunto un nuovo metodo Elimina() con un'implementazione predefinita (sebbene non abbia aggiunto il codice di implementazione effettivo a scopo dimostrativo). 

Abbiamo anche la libertà di ignorare l'implementazione predefinita, se vogliamo.

class StudentLocalDataSource: StudentRepository // ... override fun delete (studente: Studente) // do implementation

Come affermato, un'interfaccia di Kotlin può avere proprietà, ma si noti che non può mantenere lo stato. (Tuttavia, ricorda che le classi astratte possono mantenere lo stato). Pertanto, la seguente definizione di interfaccia con una dichiarazione di proprietà funzionerà.

interface StudentRepository val propFoo: Boolean // funzionerà // ...

Ma se proviamo ad aggiungere qualche stato all'interfaccia assegnando un valore alla proprietà, non funzionerà.

interface StudentRepository val propFoo: Boolean = true // Errore: gli inizializzatori di proprietà non sono consentiti nelle interfacce // ...

Tuttavia, una proprietà di interfaccia in Kotlin può avere metodi getter e setter (sebbene solo quest'ultimo se la proprietà è mutabile). Si noti inoltre che la proprietà in un'interfaccia non può avere un campo di supporto. 

interface StudentRepository var propFoo: Boolean get () = true set (valore) if (valore) // fa qualcosa // ...

Possiamo anche sostituire una proprietà dell'interfaccia se lo desideri, in modo da ridefinirlo. 

class StudentLocalDataSource: StudentRepository // ... override var propFoo: Boolean get () = false set (valore) if (valore) 

Diamo un'occhiata a un caso in cui abbiamo una classe che implementa più interfacce con la stessa firma del metodo. In che modo la classe decide quale metodo di interfaccia chiamare?

interface InterfaceA fun funD ()  interface InterfaceB fun funD () 

Qui abbiamo due interfacce che hanno un metodo con la stessa firma fondo(). Creiamo una classe che implementa queste due interfacce e sovrascrive il fondo() metodo. 

classe classA: InterfaceA, InterfaceB override fun funD () super.funD () // Errore: molti supertipi disponibili, si prega di specificare quello che intendi in parentesi angolari, ad es. 'super'

Il compilatore è confuso sulla chiamata di super.funD () metodo perché le due interfacce che le implementazioni di classe hanno la stessa firma di metodo. 

Per risolvere questo problema, avvolgiamo il nome dell'interfaccia per il quale vogliamo chiamare il metodo tra parentesi angolari . (IntelliJ IDEA o Android Studio ti daranno un suggerimento su come risolvere questo problema quando emergerà.)

class classA: InterfaceA, InterfaceB override fun funD () super.funD ()

Qui chiameremo il fondo()  metodo di InterfaceA. Problema risolto! 

3. Ereditarietà

Una nuova classe (sottoclasse) viene creata acquisendo membri di una classe esistente (superclasse) e forse ridefinendo la loro implementazione predefinita. Questo meccanismo è noto come eredità in programmazione orientata agli oggetti (OOP). Una delle cose che rendono Kotlin così impressionante è che comprende sia i paradigmi di programmazione funzionale e OOP - tutti in una lingua.

La classe base per tutte le classi in Kotlin è Qualunque

classe Persona: Qualsiasi 

Il Qualunque il tipo è equivalente al Oggetto tipo che abbiamo in Java. 

public open class Qualsiasi public open operator fun uguale a (altro: Any?): Booleano public open fun hashCode (): Int public open fun toString (): String

Il Qualunque tipo contiene i seguenti membri: è uguale a(), codice hash(), e anche accordare() metodi (simili a Java). 

Le nostre classi non hanno bisogno di estendere esplicitamente questo tipo. Se non si specifica esplicitamente in quale classe si estende una nuova classe, la classe si estende Qualunque implicitamente. Per questo motivo, in genere non è necessario includere : Qualunque nel tuo codice, lo facciamo nel codice sopra a scopo dimostrativo. 

 Diamo ora un'occhiata alla creazione di classi in Kotlin con l'ereditarietà in mente. 

classe Studente  classe LaureatoStudente: Studente () 

Nel codice sopra, il Studente laureato la classe estende la superclasse Alunno. Ma questo codice non verrà compilato. Perché? Perché le classi e i metodi sono finale di default in Kotlin. In altre parole, non possono essere estesi per impostazione predefinita, diversamente da Java, dove classi e metodi sono aperti per impostazione predefinita. 

Le migliori pratiche di ingegneria del software consigliano di iniziare a creare classi e metodi finale di default-i.e. se non sono specificatamente destinati a essere ridefiniti o sovrascritti in sottoclassi. Il team di Kotlin (JetBrains) ha applicato questa filosofia di codifica e molte altre migliori pratiche di sviluppo nello sviluppo di questo linguaggio moderno. 

Per consentire alle sottoclassi di essere create da una superclasse, dobbiamo contrassegnare esplicitamente la superclasse con la Aperto modificatore. Questo modificatore si applica anche a qualsiasi proprietà o metodo di superclasse che dovrebbe essere sovrascritto dalle sottoclassi.

open class Student 

Abbiamo semplicemente messo il Aperto modificatore prima del classe parola chiave. Ora abbiamo incaricato il compilatore di consentire il nostro Alunno classe da aprire per l'estensione. 

Come affermato in precedenza, i membri di una classe Kotlin sono anche definitivi per impostazione predefinita. 

open class Studente open fun schoolFees (): BigDecimal // do implementation

Nel codice precedente, abbiamo contrassegnato il tasse scolastiche funziona come Aperto-in modo che le sottoclassi possano sovrascriverlo. 

open class GraduateStudent: Student () override fun SchoolFees (): BigDecimal return super.schoolFees () + calculateSchoolFees () fun private calculateSchoolFees (): BigDecimal // calcola e restituisci tasse scolastiche

Qui, l'aperto tasse scolastiche funzione dalla superclasse Alunno è sovrascritto da Studente laureato class-by aggiungendo il oltrepassare modificatore prima del divertimento parola chiave. Notare che se si sostituisce un membro di una superclasse o di un'interfaccia, lo sarà anche il membro che esegue l'override Aperto per impostazione predefinita, come nell'esempio seguente:

class ComputerScienceStudent: GraduateStudent () override fun schoolFees (): BigDecimal return super.schoolFees () + calculateSchoolFess () fun private calculateSchoolFess (): BigDecimal // calcola e restituisci tasse scolastiche

Anche se non abbiamo segnato il tasse scolastiche() metodo nel Studente laureato classe con il Aperto modificatore, possiamo ancora sovrascriverlo, come abbiamo fatto nel ComputerScienceStudent classe. Per impedirlo, dobbiamo contrassegnare il membro preponderante come finale

Ricorda che possiamo aggiungere nuove funzionalità a una classe, anche se definitiva, utilizzando le funzioni di estensione in Kotlin. Per un aggiornamento sulle funzioni di estensione, controlla le mie funzioni avanzate nel post di Kotlin. Inoltre, se hai bisogno di un aggiornamento su come dare anche a una classe finale nuove proprietà senza ereditarne, leggi la sezione sulle proprietà dell'estensione nel mio post Advanced Properties and Classes. 

Se la nostra superclasse ha un costruttore principale come questo:

open class Student (val firstName: String, val lastName: String) // ...

Quindi qualsiasi sottoclasse deve chiamare il costruttore principale della superclasse. 

open class GraduateStudent (firstName: String, lastName: String): Student (firstName, lastName) // ...

Possiamo semplicemente creare un oggetto di Studente laureato classe come al solito:

val graduateStudent = GraduateStudent ("Jon", "Snow") println (graduateStudent.firstName) // Jon

Se la sottoclasse desidera chiamare il costruttore della superclasse dal suo costruttore secondario, usiamo il super parola chiave (simile a come i costruttori di superclasse vengono richiamati in Java). 

open class GraduateStudent: Student // ... private var thesis: String = "" costruttore (firstName: String, lastName: String, thesis: String): super (firstName, lastName) this.thesis = thesis

Se hai bisogno di un aggiornamento sui costruttori di classi in Kotlin, visita gentilmente il mio post su Classi e Oggetti. 

4. Bonus: digitare alias

Un'altra cosa fantastica che possiamo fare in Kotlin è dare un alias al tipo. 

Vediamo un esempio.

classe di dati Persona (val firstName: String, val lastName: String, val age: Int)

Nella classe sopra, possiamo assegnare il Stringa e Int tipi per il Persona proprietà alias utilizzando il typealias modificatore in Kotlin. Questo modificatore è usato per creare un alias di qualsiasi tipo in Kotlin, inclusi quelli che hai creato. 

typealias Name = String typealias Age = Int classe dati Person (val firstName: Name, val lastName: Name, val age: Age) 

Come puoi vedere, abbiamo creato un alias Nome e Età per entrambi Stringa e Int tipi rispettivamente. Ora abbiamo sostituito il nome di battesimo e cognome tipo di proprietà al nostro alias Nome-e anche Int digitare a Età alias. Nota che non abbiamo creato nuovi tipi: abbiamo invece creato un alias per i tipi. 

Questi possono essere utili quando si desidera fornire un significato migliore o tipi semantici al proprio codice Kotlin. Quindi usali saggiamente! 

Conclusione

In questo tutorial, hai imparato di più sulla programmazione orientata agli oggetti in Kotlin. Abbiamo coperto quanto segue:

  • classi astratte
  • interfacce 
  • eredità
  • digita alias

Se hai imparato Kotlin attraverso la nostra serie Kotlin From Scratch, assicurati di aver digitato il codice che vedi e di averlo eseguito sul tuo IDE. Un ottimo consiglio per comprendere davvero un nuovo linguaggio di programmazione (o qualsiasi altro concetto di programmazione) che stai imparando è quello di assicurarti di non solo leggere la guida o la risorsa di apprendimento, ma anche di digitare il codice reale ed eseguirlo!

Nel prossimo tutorial della serie Kotlin From Scratch, ti verrà presentata la gestione delle eccezioni in Kotlin. A presto!

Per saperne di più sulla lingua di Kotlin, ti consiglio di visitare la documentazione di Kotlin. Oppure guarda alcuni dei nostri altri post di sviluppo di app per Android qui su Envato Tuts!