DEV Community

Armando Picón
Armando Picón

Posted on • Originally published at Medium on

Kotlin —Aprovechando mejor @Kotlin

Si vienes de Java a Kotlin y te interesa aprovechar mejor este lenguaje considera los siguientes consejos.

Emplea expresiones

Podríamos tener el siguiente bloque de _if_s:

**fun** getDefaultLocale(deliveryArea: String): Locale{
**val** deliveryAreaLower = deliveryArea._toLowerCase_()
**if** (deliveryAreaLower == **"mexico"** || deliveryAreaLower == **"peru"** ){
**return** MyLocale. **SPANISH**  

}
**if** (deliveryAreaLower == **"brazil"** ){
**return** MyLocale. **PORTUGUESE**  

}
**if** (deliveryAreaLower == **"usa"** ){
**return** MyLocale. **ENGLISH**  
}

**return** MyLocale. **SPANISH**  
}

Esto podría simplificarse de la siguiente manera haciendo uso de when:

**fun** getDefaultLocale(deliveryArea: String) = **when** (deliveryArea._toLowerCase_()) {
**"mexico"** , **"peru"** -> MyLocale.**SPANISH  
    "brazil" **-> MyLocale.** PORTUGUESE  
    "usa" **-> MyLocale.** ENGLISH  
    else **-> MyLocale.** SPANISH**  
}

Es recomendable evaluar si puedes usar “when” como reemplazo de varios “if”.

Emplear Extension Functions para funciones utilitarias

En Java acostumbramos a generar nuestras clases utilitarias con funciones estáticas de la siguiente manera:

**public class** StringUtils {
**public static int** countAmountOfX(String string){
**return** string.length() - string.replace( **"x"** , **""** ).length();
    }
}

Su conversión natural a Kotlin sería de esta manera:

**object** StringUtils {
**fun** countAmountOfX(string: String): Int {
**return** string. **length** - string._replace_( **"x"** , **""** ). **length**  
}
}

Sin embargo, Kotlin nos permite prescindir del objeto que envuelve nuestra función, ya que las funciones son elementos de primer nivel en Kotlin (o en otras palabras puedes escribir una función en un archivo Kotlin sin necesidad de envolverlo con una clase como en Java):

**fun** countAmountOfX(string: String): Int {
**return** string. **length** - string._replace_( **"x"** , **""** ). **length**  
}

Incluso, dependiendo de la función podríamos convertirla en una “extension function”:

**fun** String.countAmountOfX(): Int {
**return this**. **length** - **this**._replace_( **"x"** , **""** ). **length**  
}

**fun** main(args: Array<String>) {
_// Nos dará 4  
    println_ ( **"xEstoxEsxKotlinx"**._countAmountOfX_())
}

También te hablo sobre Extension functions en el siguiente

Considera emplear apply()

Algunas veces tenemos esta clase de funciones en Java:

**public** File makeDir(String path){
    File result = **new** File(path);
    result.mkdirs();
**return** result;
}

La cual en Kotlin se traduciría de la siguiente manera:

**fun** makeDir(path: String): File {
**val** result = File(path)
    result.mkdirs()
**return** result
}

Consideremos el hecho de que se está declarando una variable sobre la que se están aplicando algunas funciones y retornando la misma variable al final. Esto se podría simplificar mediante el uso de la función apply().

**fun** makeDir(path: String) = File(path)._apply_ **{** mkdirs() **}**

Podrías aplicar apply() también en el siguiente escenario, estamos poblando un datasource de la siguiente manera:

**fun** getDataSource(): MysqlDataSource {
**val** fileInputStream = FileInputStream( **"myfile.properties"** )

**val** properties = Properties()
    properties.load(fileInputStream)

**val** mySqlDataSource = MysqlDataSource()
    mySqlDataSource.setURL(properties.getProperty( **"MYSQL\_DB\_URL"** ))
    mySqlDataSource._user_ = properties.getProperty( **"MYSQL\_DB\_USERNAME"** )
    mySqlDataSource.setPassword(properties.getProperty( **"MYSQL\_DB\_PASSWORD"** ))
    mySqlDataSource._port_ = properties.getProperty( **"MYSQL\_DB\_PORT"** )._toInt_()
    mySqlDataSource._databaseName_ = properties.getProperty( **"MYSQL\_DB\_NAME"** )
    mySqlDataSource._maxQuerySizeToLog_ = properties.getProperty( **"MYSQL\_DB\_QUERYSIZE"** )._toInt_()

**return** mySqlDataSource
}

Podemos agrupar el seteo de las propiedades del objeto mySqlDataSource, y evitamos de paso repetir la misma variable varias veces, de la siguiente forma:

**val** mySqlDataSource = MysqlDataSource()._apply_ **{**  
setURL(properties.getProperty( **"MYSQL\_DB\_URL"** ))
_user_ = properties.getProperty( **"MYSQL\_DB\_USERNAME"** )
    setPassword(properties.getProperty( **"MYSQL\_DB\_PASSWORD"** ))
_port_ = properties.getProperty( **"MYSQL\_DB\_PORT"** )._toInt_()
_databaseName_ = properties.getProperty( **"MYSQL\_DB\_NAME"** )
_maxQuerySizeToLog_ = properties.getProperty( **"MYSQL\_DB\_QUERYSIZE"** )
**}**

Evita la sobrecarga de funciones para argumentos por defecto

Suelen ser útiles en Java, pero no hay necesidad de recurrir a lo siguiente para trabajar con argumentos por defecto:

**fun** findPhoneNumber(number:String):String{
**return** findPhoneNumber(number, **"MX"** )
}

**fun** findPhoneNumber(number: String, country: String): String {
_TODO_( **"Implementa tu búsqueda"** )
}

Esto lo podemos simplificar aplicando un valor por defecto al argumento correspondiente (o a ambos si así lo quisieramos):

**fun** findPhoneNumber(number: String, locale: String = **"MX"** ): String {
_TODO_( **"Implementa tu búsqueda"** )
}

De este modo podríamos invocar a la función findPhoneNumber enviando un solo parámetro y automáticamente Kotlin empleará para el segundo parámetro el valor que declaramos por defecto.

Reemplaza la validación de null por let()

Considera la siguiente validación:

private fun insertDataIntoDatabase(db: SQLiteDatabase?, comicValues: MutableList<ContentValues>) {
    if (db != null) {
        try {
            db.beginTransaction()
            comicValues._forEach_ **{** db.insert(ComicContract.ComicEntry.TABLE\_NAME, null, **it** ) **}**  
db.setTransactionSuccessful()
        } catch (e: SQLException) {
            Log.e(_javaClass_._simpleName_, **"Ha ocurrido un error durante la inserción."** , e)
        } finally {
            db.endTransaction()
        }
    }
}

En el interior de esta función estamos validando si el argumento recibido db es nulo o no, como somos nuevos en Kotlin podríamos considerar reescribir esta función haciendo uso del operador ?. para evitar la validación anterior.

try {
    db?.beginTransaction()
    comicValues._forEach_ **{** db?.insert(ComicContract.ComicEntry.TABLE\_NAME, null, **it** ) **}**  
db?.setTransactionSuccessful()
} catch (e: SQLException) {
    Log.e(_javaClass_._simpleName_, **"Ha ocurrido un error durante la inserción."** , e)
} finally {
    db?.endTransaction()
}

Sin embargo, una mejor manera de hacerlo sería empleando let() de la siguiente forma:

db?._let_ **{**  
try {
        db.beginTransaction()
        comicValues._forEach_ **{** db.insert(ComicContract.ComicEntry.TABLE\_NAME, null, **it** ) **}**  
db.setTransactionSuccessful()
    } catch (e: SQLException) {
        Log.e(_javaClass_._simpleName_, **"Ha ocurrido un error durante la inserción."** , e)
    } finally {
        db.endTransaction()
    }            
**}**

La función let() además nos permite crear bloques de código, es decir, toda variable que se creara dentro de este bloque solo residiría en él evitando algún problema que se pudiera suscitar manteniendo dichas variables más allá de donde solo se requieren.

Nota final

Estas son algunas de las formas de mejorar tu código en Kotlin, con el transcurso de los días iré sumando algunos más; pero por lo pronto estas son algunas mejoras que tuve que ir mejorando en mi paso de Java a Kotlin.

Pueden encontrar el gist con código de este artículo en mi repositorio en Github.

¡Gracias por leer el artículo, significa mucho para mi! Si lo disfrutaste o fue de utilidad por favor recomiéndalo mediante el ícono del corazón ❤ y compartelo con tus amigos.

Me puedes encontrar en Twitter y en Github.


Top comments (0)