Questo argomento si concentra su alcuni degli aspetti più utili della lingua kotlin durante lo sviluppo per Android.
Utilizzo dei frammenti
Le sezioni seguenti utilizzano esempi di Fragment
per mettere in evidenza alcune delle caratteristiche di Kotlin
le migliori funzionalità.
Ereditarietà
Puoi dichiarare una classe in Kotlin con la parola chiave class
. Nel seguente
Ad esempio, LoginFragment
è una sottoclasse di Fragment
. Puoi indicare
ereditarietà utilizzando l'operatore :
tra la sottoclasse e la relativa classe padre:
class LoginFragment : Fragment()
In questa dichiarazione del corso, LoginFragment
è responsabile della chiamata al metodo
costruttore della sua superclasse, Fragment
.
All'interno di LoginFragment
, puoi eseguire l'override di una serie di callback del ciclo di vita
Rispondere ai cambiamenti di stato nel tuo Fragment
. Per eseguire l'override di una funzione, utilizza
override
parola chiave, come mostrato nell'esempio seguente:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
Per fare riferimento a una funzione nella classe principale, utilizza la parola chiave super
, come mostrato
nel seguente esempio:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
Nullability e inizializzazione
Negli esempi precedenti, alcuni parametri dei metodi sottoposti a override hanno
tipi con il suffisso ?
. Ciò indica che gli argomenti
passati per questi parametri possono essere nulli. Assicurati di
gestire in modo sicuro i propri file nulli.
In Kotlin, devi inizializzare le proprietà di un oggetto quando dichiari l'oggetto.
Ciò implica che quando ottieni un'istanza di una classe, puoi immediatamente
far riferimento a qualsiasi sua proprietà accessibile. Gli oggetti View
in un Fragment
,
ma non sono pronti per essere gonfiato fino a quando non chiami Fragment#onCreateView
, quindi
devi trovare un modo per rinviare l'inizializzazione delle proprietà per un View
.
lateinit
ti consente di rimandare l'inizializzazione delle proprietà. Quando usi lateinit
,
dovresti inizializzare la tua proprietà
il prima possibile.
L'esempio seguente mostra l'utilizzo di lateinit
per assegnare View
oggetti in
onViewCreated
:
class LoginFragment : Fragment() {
private lateinit var usernameEditText: EditText
private lateinit var passwordEditText: EditText
private lateinit var loginButton: Button
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}
...
}
Conversione da SAM
Puoi rimanere in ascolto degli eventi di clic in Android implementando il parametro
interfaccia di OnClickListener
. Button
di oggetti contengono un setOnClickListener()
che accetta un'implementazione di OnClickListener
.
OnClickListener
ha un singolo metodo astratto, onClick()
, che devi
da implementare. Perché setOnClickListener()
accetta sempre un OnClickListener
come
un argomento e poiché OnClickListener
ha sempre lo stesso singolo astratto
questa implementazione può essere rappresentata utilizzando una funzione anonima
Kotlin. Questo processo è noto come
conversione di un metodo astratto singolo,
o conversione da SAM.
La conversione da SAM può rendere il tuo codice notevolmente più pulito. Nell'esempio che segue
mostra come utilizzare la conversione SAM per implementare OnClickListener
per un
Button
:
loginButton.setOnClickListener {
val authSuccessful: Boolean = viewModel.authenticate(
usernameEditText.text.toString(),
passwordEditText.text.toString()
)
if (authSuccessful) {
// Navigate to next screen
} else {
statusTextView.text = requireContext().getString(R.string.auth_failed)
}
}
Il codice all'interno della funzione anonima passata a setOnClickListener()
viene eseguito quando un utente fa clic su loginButton
.
Oggetti associati
Oggetti companion
forniscono un meccanismo per definire variabili o funzioni collegate
concettualmente a un tipo, ma non sono legati a un particolare oggetto. modalità Complementare
sono simili all'utilizzo della parola chiave static
di Java per variabili e metodi.
Nell'esempio seguente, TAG
è una costante String
. Non è necessario un ID univoco
di String
per ogni istanza di LoginFragment
, quindi dovresti
definiscilo in un oggetto associato:
class LoginFragment : Fragment() {
...
companion object {
private const val TAG = "LoginFragment"
}
}
Puoi definire TAG
al livello superiore del file, ma
potrebbe anche avere un gran numero di variabili, funzioni e classi
definiti anche al livello superiore. Gli oggetti companion aiutano a connettersi
variabili, funzioni e la definizione della classe senza fare riferimento ad alcuna
particolare istanza di quella classe.
Delega proprietà
Durante l'inizializzazione delle proprietà, potresti ripetere alcune delle operazioni più comuni
pattern, come l'accesso a un ViewModel
all'interno di un Fragment
. Per evitare l'eccesso
codice duplicato, puoi usare la sintassi della delega delle proprietà di Kotlin.
private val viewModel: LoginViewModel by viewModels()
La delega della proprietà fornisce un'implementazione comune che puoi riutilizzare
in tutta l'app. Android KTX fornisce alcuni delegati per le proprietà.
viewModels
, ad esempio, recupera un ViewModel
che ha come ambito
Fragment
corrente.
La delega delle proprietà utilizza la riflessione, che aumenta il sovraccarico del rendimento. La soluzione migliore è una sintassi concisa che fa risparmiare tempo nello sviluppo.
Nullabilità
Kotlin fornisce rigide regole con supporto dei valori null che mantengono la sicurezza dei tipi ovunque.
la tua app. In Kotlin, i riferimenti agli oggetti non possono contenere valori nulli
predefinito. Per assegnare un valore nullo a una variabile, devi dichiarare un valore nullable.
di variabile aggiungendo ?
alla fine del tipo di variabile.
Ad esempio, la seguente espressione non è valida in Kotlin. name
è di tipo
String
e non è possibile assegnare valori null:
val name: String = null
Per consentire un valore nullo, devi utilizzare un tipo String
con valore nullo, String?
, come
come mostrato nell'esempio seguente:
val name: String? = null
Interoperabilità
Le rigide regole di Kotlin rendono il tuo codice più sicuro e conciso. Queste regole abbassano
la possibilità che l'app abbia un NullPointerException
in modo anomalo. Inoltre, riducono il numero di controlli nulli che devi effettuare nei tuoi
le API nel tuo codice.
Spesso, devi chiamare anche un codice diverso da Kotlin quando scrivi un'app per Android, la maggior parte delle API Android è scritta nel linguaggio di programmazione Java.
Nullability è un'area chiave in cui Java e Kotlin hanno un comportamento diverso. Il prezzo di Java è inferiore rigorosamente con sintassi con nullità.
Ad esempio, la classe Account
ha alcune proprietà, tra cui una String
denominata name
. Java non ha le regole di Kotlin sui valori nulli,
affidarsi invece alle annotazioni di nullità facoltative per dichiarare esplicitamente
se puoi assegnare un valore nullo.
Poiché il framework Android è scritto principalmente in Java, potresti incontrare questo scenario quando chiami nelle API senza annotazioni con valore nullo.
Tipi di piattaforma
Se utilizzi Kotlin per fare riferimento a un membro name
non annotato definito in un
Java Account
, il compilatore non sa se String
è mappato a una
String
o String?
in Kotlin. Questa ambiguità è rappresentata da un
tipo di piattaforma, String!
.
String!
non ha un significato speciale per il compilatore Kotlin. String!
può rappresentare
String
o String?
e il compilatore ti consente di assegnare il valore
per entrambi i tipi. Tieni presente che rischi di lanciare un NullPointerException
se
rappresenta il tipo come String
e assegna un valore nullo.
Per risolvere questo problema, devi utilizzare annotazioni nulle ogni volta che scrivi in Java. Queste annotazioni sono utili per gli sviluppatori Java e Kotlin.
Ad esempio, ecco la classe Account
definita in Java:
public class Account implements Parcelable {
public final String name;
public final String type;
private final @Nullable String accessId;
...
}
Una delle variabili membro, accessId
, è annotata con @Nullable
,
a indicare che può contenere un valore nullo. In questo modo Kotlin tratterebbe accessId
come String?
.
Per indicare che una variabile non può mai essere nulla, utilizza l'annotazione @NonNull
:
public class Account implements Parcelable {
public final @NonNull String name;
...
}
In questo scenario, name
è considerato un String
senza valori null in Kotlin.
Le annotazioni di nullità sono incluse in tutte le nuove API Android e in molte API Android. Molte librerie Java hanno aggiunto annotazioni null per migliorare supportare sviluppatori sia Kotlin che Java.
Gestione dei valori null
Se hai dubbi su un tipo Java, considera che sia possibile specificare valori null.
Ad esempio, il membro name
della classe Account
non è annotato, quindi
deve assumere che sia un String
con valori null.
Se vuoi tagliare name
in modo che il suo valore non includa iniziali o
uno spazio vuoto finale, puoi usare la funzione trim
di Kotlin. Puoi tagliare in sicurezza
String?
in diversi modi. Uno di questi modi è utilizzare la funzione not-null
operatore asserzione, !!
, come mostrato nell'esempio seguente:
val account = Account("name", "type")
val accountName = account.name!!.trim()
L'operatore !!
tratta tutto ciò che si trova sul lato sinistro come non null, quindi
in questo caso, stai trattando name
come un String
non nullo. Se il risultato
a sinistra è nullo, l'app genera un NullPointerException
.
Si tratta di un operatore rapido e semplice, ma va usato con parsimonia perché
reintroduci le istanze di NullPointerException
nel codice.
Una scelta più sicura consiste nell'utilizzare l'operatore di chiamata sicura, ?.
, come mostrato nell'
nell'esempio seguente:
val account = Account("name", "type")
val accountName = account.name?.trim()
Utilizzando l'operatore di chiamata sicura, se name
è diverso da null, il risultato di
name?.trim()
è un valore del nome senza spazi vuoti iniziali o finali. Se
name
è nullo, il risultato di name?.trim()
è null
. Ciò significa che
la tua app non può mai generare un NullPointerException
quando esegui questa istruzione.
Anche se l'operatore della chiamata sicura ti evita di avere un potenziale NullPointerException
,
ma passa un valore nullo all'istruzione successiva. Puoi gestire un valore nullo
direttamente utilizzando un operatore Elvis (?:
), come mostrato
esempio:
val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"
Se il risultato dell'espressione sul lato sinistro dell'operatore Elvis
null, il valore sul lato destro è assegnato a accountName
. Questo
è utile per fornire un valore predefinito che altrimenti sarebbe nullo.
Puoi anche utilizzare l'operatore Elvis per tornare in anticipo da una funzione, come mostrato nel seguente esempio:
fun validateAccount(account: Account?) {
val accountName = account?.name?.trim() ?: "Default name"
// account cannot be null beyond this point
account ?: return
...
}
Modifiche all'API Android
Le API Android stanno diventando sempre più compatibili con Kotlin. Molti dei dispositivi Android
le API più comuni, tra cui AppCompatActivity
e Fragment
, contengono
annotazioni NULL e alcune chiamate come Fragment#getContext
hanno
più alternative compatibili con Kotlin.
Ad esempio, se accedi all'Context
di un Fragment
è quasi sempre non null,
poiché la maggior parte delle chiamate effettuate in Fragment
avviene mentre Fragment
è collegata a Activity
(una sottoclasse Context
). Detto questo,
Fragment#getContext
non restituisce sempre un valore diverso da null, in quanto sono presenti
scenari in cui un Fragment
non è associato a un Activity
. Di conseguenza, il ritorno
il tipo di Fragment#getContext
è null.
Poiché il valore Context
restituito da Fragment#getContext
è nullo (e è
annotato come @Nullable), devi considerarlo come Context?
nel tuo codice Kotlin.
Ciò significa applicare uno degli operatori citati in precedenza per
con valore nullo prima di accedere alle sue proprietà e funzioni. Per alcuni di questi
Android contiene API alternative che offrono questa convenienza.
Fragment#requireContext
, ad esempio, restituisce un Context
non nullo e restituisce
un IllegalStateException
se chiamato quando un Context
è null. In questo modo
puoi trattare il valore Context
risultante come non null senza dover
operatori o soluzioni alternative per le chiamate sicure.
Inizializzazione della proprietà
Le proprietà in Kotlin non sono inizializzate per impostazione predefinita. Devono essere inizializzati quando la classe che lo contiene viene inizializzata.
Puoi inizializzare le proprietà in diversi modi. Nell'esempio che segue
mostra come inizializzare una variabile index
assegnandole un valore nel
dichiarazione del corso:
class LoginFragment : Fragment() {
val index: Int = 12
}
Questa inizializzazione può essere definita anche in un blocco di inizializzazione:
class LoginFragment : Fragment() {
val index: Int
init {
index = 12
}
}
Negli esempi precedenti, index
viene inizializzato quando un LoginFragment
viene
creato.
Tuttavia, potresti avere alcune proprietà che non possono essere inizializzate durante l'oggetto
edilizia. Ad esempio, potresti voler fare riferimento a un View
dall'interno di una
Fragment
, quindi prima il layout deve essere gonfiato. L'inflazione
non si verificano quando viene creato un Fragment
. È invece gonfiato quando chiami
Fragment#onCreateView
.
Un modo per risolvere questo scenario è dichiarare la vista come null e inizializzalo il prima possibile, come mostrato nell'esempio seguente:
class LoginFragment : Fragment() {
private var statusTextView: TextView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
}
Anche se funziona come previsto, ora devi gestire il valore null dell'View
ogni volta che ci fai riferimento. Una soluzione migliore è utilizzare lateinit
per View
come mostrato nell'esempio seguente:
class LoginFragment : Fragment() {
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
La parola chiave lateinit
ti consente di evitare di inizializzare una proprietà quando
in cui viene creato l'oggetto. Se viene fatto riferimento alla tua proprietà prima di essere inizializzata,
Kotlin lancia un UninitializedPropertyAccessException
, quindi assicurati di
inizializzare la proprietà il prima possibile.