Passare a una nuova lingua può essere un compito arduo. La ricetta per il successo è iniziare lentamente, procedere per blocchi e testare frequentemente per allineare il team al successo. Kotlin semplifica la migrazione, in quanto viene compilato in bytecode JVM ed è completamente interoperabile con Java.
Creazione del team
Il primo passo prima della migrazione è creare una base di comprensione comune per il tuo team. Ecco alcuni suggerimenti che potrebbero esserti utili per accelerare l'apprendimento del tuo team.
Formare gruppi di studio
I gruppi di studio sono un modo efficace per facilitare l'apprendimento e la memorizzazione. Gli studi suggeriscono che ripetere ciò che hai imparato in un contesto di gruppo aiuta a consolidare le informazioni. Acquista un libro su Kotlin o altro materiale di studio per ogni membro del gruppo e chiedi al gruppo di leggere un paio di capitoli ogni settimana. Durante ogni incontro, il gruppo deve confrontare ciò che ha imparato e discutere di eventuali domande o osservazioni.
Creare una cultura dell'insegnamento
Anche se non tutti si considerano insegnanti, tutti possono insegnare. Dal responsabile di un team o di una tecnologia al singolo collaboratore, tutti possono incoraggiare un ambiente di apprendimento che possa contribuire a garantire il successo. Un modo per facilitare questo processo è organizzare presentazioni periodiche in cui una persona del team è incaricata di parlare di qualcosa che ha imparato o che vuole condividere. Puoi sfruttare il tuo gruppo di studio chiedendo a dei volontari di presentare un nuovo capitolo ogni settimana finché il tuo team non si sentirà a suo agio con la lingua.
Designare un campione
Infine, nomina un campione che guidi un'iniziativa di apprendimento. Questa persona può fungere da esperto in materia (SME) all'inizio della procedura di adozione. È importante includere questa persona in tutte le tue riunioni di pratica relative a Kotlin. Idealmente, questa persona è già appassionata di Kotlin e ha una certa conoscenza pratica.
Integra lentamente
È fondamentale iniziare lentamente e pensare in modo strategico a quali parti dell'ecosistema spostare per prime. Spesso è meglio isolare questa operazione in una singola app all'interno dell'organizzazione anziché in un'app di punta. Per quanto riguarda la migrazione dell'app scelta, ogni situazione è diversa, ma ecco alcuni punti di partenza comuni.
Modello dei dati
Il tuo modello di dati probabilmente è costituito da molte informazioni sullo stato e da alcuni
metodi. Il modello dati potrebbe anche avere metodi comuni come toString(),
equals() e hashcode(). In genere, è possibile eseguire facilmente la transizione e il test
delle unità in isolamento.
Ad esempio, supponiamo il seguente snippet di Java:
public class Person {
private String firstName;
private String lastName;
// ...
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Puoi sostituire la classe Java con una singola riga di Kotlin, come mostrato di seguito:
data class Person(var firstName: String?, var lastName: String?)
Questo codice può quindi essere sottoposto a test delle unità rispetto alla tua attuale suite di test. L'idea è di iniziare con un modello alla volta e di eseguire la transizione delle classi che sono principalmente state e non comportamento. Assicurati di eseguire test frequenti durante il percorso.
Esegui la migrazione dei test
Un altro percorso iniziale da prendere in considerazione è la conversione dei test esistenti e l'inizio della scrittura di nuovi test in Kotlin. In questo modo, il tuo team avrà il tempo di familiarizzare con il linguaggio prima di scrivere il codice che intendi includere nella tua app.
Sposta i metodi di utilità nelle funzioni di estensione
Qualsiasi classe di utilità statica (StringUtils, IntegerUtils, DateUtils,
YourCustomTypeUtils e così via) può essere rappresentata come
funzioni di estensione Kotlin
e utilizzata dal codebase Java esistente.
Ad esempio, supponiamo di avere una classe StringUtils con alcuni metodi:
package com.java.project;
public class StringUtils {
public static String foo(String receiver) {
return receiver...; // Transform the receiver in some way
}
public static String bar(String receiver) {
return receiver...; // Transform the receiver in some way
}
}
Questi metodi potrebbero poi essere utilizzati altrove nella tua app, come mostrato nell'esempio seguente:
...
String myString = ...
String fooString = StringUtils.foo(myString);
...
Utilizzando le funzioni di estensione Kotlin, puoi fornire la stessa interfaccia Utils ai chiamanti Java, offrendo al contempo un'API più concisa per la tua base di codice Kotlin in crescita.
Per farlo, potresti iniziare a convertire questa classe Utils in Kotlin utilizzando la
conversione automatica fornita dall'IDE. L'output di esempio potrebbe essere simile al seguente:
package com.java.project
object StringUtils {
fun foo(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
fun bar(receiver: String): String {
return receiver...; // Transform the receiver in some way
}
}
Successivamente, rimuovi la definizione di classe o oggetto, aggiungi il prefisso a ogni nome di funzione con il tipo a cui deve essere applicata questa funzione e utilizzalo per fare riferimento al tipo all'interno della funzione, come mostrato nell'esempio seguente:
package com.java.project
fun String.foo(): String {
return this...; // Transform the receiver in some way
}
fun String.bar(): String {
return this...; // Transform the receiver in some way
}
Infine, aggiungi un'annotazione JvmName nella parte superiore del file sorgente per rendere il nome compilato compatibile con il resto dell'app, come mostrato nell'esempio seguente:
@file:JvmName("StringUtils")
package com.java.project
...
La versione finale dovrebbe essere simile alla seguente:
@file:JvmName("StringUtils")
package com.java.project
fun String.foo(): String {
return this...; // Transform `this` string in some way
}
fun String.bar(): String {
return this...; // Transform `this` string in some way
}
Tieni presente che queste funzioni ora possono essere chiamate utilizzando Java o Kotlin con convenzioni che corrispondono a ogni linguaggio.
Kotlin
// ... val myString: String = "hello" val fooString = myString.foo() // ...
Java
... String myString = ... String fooString = StringUtils.foo(myString); ...
Completare la migrazione
Una volta che il tuo team ha familiarità con Kotlin e hai eseguito la migrazione di aree più piccole,
puoi passare ad affrontare componenti più grandi come fragment, attività,
oggetti ViewModel e altre classi correlate alla logica di business.
Considerazioni
Proprio come Java ha uno stile specifico, Kotlin ha il suo stile idiomatico che contribuisce alla sua concisione. Tuttavia, all'inizio potresti notare che il codice Kotlin prodotto dal tuo team assomiglia di più al codice Java che sta sostituendo. Questo valore cambia nel tempo man mano che l'esperienza del tuo team con Kotlin aumenta. Ricorda, il cambiamento graduale è la chiave del successo.
Ecco alcune cose che puoi fare per ottenere coerenza man mano che la tua base di codice Kotlin cresce:
Standard di codifica comuni
Assicurati di definire un insieme standard di convenzioni di codifica all'inizio del processo di adozione. Puoi discostarti dalla guida di stile Kotlin di Android quando ha senso.
Strumenti di analisi statica
Applica gli standard di codifica impostati per il tuo team utilizzando Android Lint e altri strumenti di analisi statica. Klint, un linter Kotlin di terze parti, fornisce anche regole aggiuntive per Kotlin.
Integrazione continua
Assicurati di rispettare gli standard di codifica comuni e di fornire una copertura di test sufficiente per il tuo codice Kotlin. L'integrazione di questa parte in un processo di build automatizzato può contribuire a garantire la coerenza e il rispetto di questi standard.
Interoperabilità
Kotlin interagisce con Java senza problemi per la maggior parte, ma tieni presente quanto segue.
Supporto di valori Null
Kotlin si basa su annotazioni con supporto di valori null nel codice compilato per dedurre la nullabilità
sul lato Kotlin. Se le annotazioni non vengono fornite, Kotlin utilizza per impostazione predefinita un tipo di piattaforma che può essere trattato come tipo nullable o non nullable. Se non trattati con attenzione, questi
possono causare problemi di runtime NullPointerException.
Adottare nuove funzionalità
Kotlin fornisce molte nuove librerie e zucchero sintattico per ridurre il boilerplate, il che contribuisce ad aumentare la velocità di sviluppo. Detto questo, fai attenzione e procedi con metodo quando utilizzi le funzioni della libreria standard di Kotlin, come le funzioni di raccolta, le coroutine e le espressioni lambda.
Ecco una trappola molto comune in cui incappano i nuovi sviluppatori Kotlin. Supponiamo il seguente codice Kotlin:
val nullableFoo: Foo? = Foo() // This lambda executes only if nullableFoo is not null // and `foo` is of the non-nullable Foo type nullableFoo?.let { foo -> foo.baz() foo.zap() }
L'intento di questo esempio è eseguire foo.baz() e foo.zap() se
nullableFoo non è null, evitando così un NullPointerException. Sebbene questo
codice funzioni come previsto, è meno intuitivo da leggere rispetto a un semplice controllo di nullità e a un
smart cast,
come mostrato nell'esempio seguente:
val nullableFoo: Foo? = null if (nullableFoo != null) { nullableFoo.baz() // Using !! or ?. isn't required; the Kotlin compiler infers non-nullability nullableFoo.zap() // from guard condition; smart casts nullableFoo to Foo inside this block }
Test
Per impostazione predefinita, le classi e le relative funzioni sono chiuse per l'estensione in Kotlin. Devi aprire esplicitamente le classi e le funzioni di cui vuoi creare una sottoclasse. Questo comportamento è una decisione di progettazione del linguaggio scelta per promuovere la composizione rispetto all'ereditarietà. Kotlin offre il supporto integrato per implementare il comportamento tramite la delega per semplificare la composizione.
Questo comportamento pone un problema per i framework di simulazione, come Mockito, che si basano sull'implementazione o sull'ereditarietà dell'interfaccia per eseguire l'override dei comportamenti durante il test. Per i test unitari, puoi attivare l'utilizzo della funzionalità Mock Maker Inline di Mockito, che ti consente di simulare classi e metodi finali. In alternativa, puoi utilizzare il plug-in del compilatore All-Open per aprire qualsiasi classe Kotlin e i relativi membri che vuoi testare nell'ambito del processo di compilazione. Il vantaggio principale dell'utilizzo di questo plug-in è che funziona sia con i test unitari sia con i test strumentati.
Ulteriori informazioni
Per saperne di più sull'utilizzo di Kotlin, consulta i seguenti link:
- Approccio Kotlin-first di Android
- Risorse per iniziare a utilizzare Kotlin
- Risorse per gli utenti Java che imparano Kotlin
- Percorso di apprendimento da Java a Kotlin, una raccolta di risorse che aiutano i programmatori Java a imparare e scrivere Kotlin in modo idiomatico.