Una de las grandes maravillas de Kotlin es que está totalmente integrado con Java. Esto quiere decir que aunque todo el código de tu aplicación sea Java, puedes crearte una clase en Kotlin y utilizarla desde Java sin problema.
Esto te da potencialmente dos ventajas:
- Puedes usar Kotlin en un proyecto Java: en cualquier proyecto que tengas ya empezado, puedes decidir empezar a hacer el código nuevo en Kotlin. Desde Java podrás acceder a él.
- Si te bloqueas en Kotlin, puedes hacer esa parte en Java: mucha gente me pregunta si hay algún caso en el que Kotlin no será suficiente para hacer algo en Android. Teóricamente todo se puede, pero en la práctica no hay forma de saberlo (nadie lo ha hecho “todo” en Android con Kotlin). El caso es que no importa, si no consigues hacerlo en Kotlin, puedes implementar esa parte en Java.
Hoy vamos a ver cómo funciona esta compatibilidad, y qué pinta tiene el código de Kotlin en Java. Y si quieres verlo en vídeo, en mi canal de YouTube tengo uno donde cubro el tema:
Si quieres aprender cuál es la situación de Kotlin en el mercado, por qué debes aprenderlo y los primero pasos para que veas lo fácil que es, he preparado un training gratuito de hora y media al que puedes unirte haciendo click aquí
Funciones a nivel de paquete
En Kotlin, las funciones no necesitan estar dentro de una clase, pero esto no ocurre así en Java. ¿Cómo podemos llamar a una función entonces? Imaginemos que tenemos un fichero utils.kt
con la siguiente pinta:
fun logD(message: String) { Log.d("", message) } fun logE(message: String) { Log.e("", message) }
En Java podremos acceder a ellos mediante una clase que llamará UtilsKt
, con métodos estáticos:
UtilsKt.logD("Debug"); UtilsKt.logE("Error");
Ya has visto por artículos anteriores que me encantan las funciones de extensión. ¿Qué pinta tienen en Java? Imaginemos que tenemos la siguiente:
fun ViewGroup.inflate(resId: Int, attachToRoot: Boolean = false): View { return LayoutInflater.from(context).inflate(resId, this, attachToRoot) }
Nota: aunque puede que hayan aparecido en algún momento, no lo he comentado explícitamente. Los argumentos de una función pueden tener valores por defecto. Esto significa que si no los especificamos, tomarán el valor que se indica en la declaración. Esto nos evita la sobrecarga de métodos que solemos utilizar en Java
La función se aplica sobre un ViewGroup
. Recibe un layout y lo infla utilizando la vista padre.
¿Qué obtendríamos si lo queremos utilizar en Java?
View v = UtilsKt.inflate(parent, R.layout.view_item, false);
Como ves, el objeto sobre el que se aplica se añade como argumento de la función. Además, el argumento opcional se vuelve obligatorio, pues en Java no tenemos esa opción.
Si quieres que genere las sobrecargas en Java, puedes utilizar la anotación @JvmOverloads
delante de la función. Esto nos permitiría no necesitar especificar el false
en Java:
View v = UtilsKt.inflate(parent, R.layout.view_item);
Si por lo que sea prefieres darle un nombre más característico a la clase que se mostrará en Java, puedes usar una anotación para modificarlo. En el fichero de utils.kt
añade antes del package
:
@file:JvmName("AndroidUtils")
Y ahora la clase en Java se llamará así:
AndroidUtils.logD("Debug"); AndroidUtils.logE("Error"); View v = AndroidUtils.inflate(parent, R.layout.view_item, false);
Campos de instancia y estáticos
En Java utilizamos campos para almacenar el estado. Pueden ser campos de instancia, lo que implica que cada clase tendrá los suyos, o estáticos (todos los comparten).
Si intentamos buscar un mapeo de esto en Kotlin, serían las propiedades y los companion objects. Si tenemos una clase así:
class App : Application() { val appHelper = AppHelper() companion object { lateinit var instance: App } override fun onCreate() { super.onCreate() instance = this } }
¿Cómo funcionará esto en Java? Simplemente puedes acceder a los objetos del companion como si fueran campos estáticos, y a las properties mediante getters
y setters
:
AppHelper helper = App.instance.getAppHelper();
Y verás que el compilador no se queja. Al ser val
, sólo nos genera el getter
en Java. Si fuera var
, también tendríamos un setter
.
El acceso a instance
ha funcionado automáticamente porque usa la anotación lateinit
, que automáticamente expone el campo que Kotlin usa por detrás para almacenar el estado. Pero si por ejemplo usamos creamos una constante:
companion object { lateinit var instance: App val CONSTANT = 27 }
Verás que no puedes acceder a ella. Para exponer el campo, necesitarás una nueva anotación:
@JvmField val CONSTANT = 27
Y ahora ya puedes utilizarla desde código Java:
int c = App.CONSTANT;
Si tuvieras funciones en un companion object, se convierten en métodos estáticos usando la anotación @JvmStatic.
Conclusión
Ya ves que es sencillísimo utilizar el código Kotlin en Java. Aquí te he mostrado algunos de los casos más típicos, pero todo se consigue de forma muy similar.
Si todo esto te apasiona tanto como a mí, te animo a que te apuntes a mi training gratuito dondé te contaré todo lo que necesitas para aprender a crear tus Apps Android en Kotlin desde cero.
0 comentarios