Introducción
Una de las mayores fortalezas de Kotlin es la interoperabilidad perfecta con Java. Puedes llamar código Java desde Kotlin y viceversa sin ningún problema.
Llamando Java desde Kotlin
Clases Java:
// Clase Java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
}
Usando en Kotlin:
val persona = Person("Alicia", 25)
println(persona.name) // Llama getName()
persona.name = "Bob" // Llama setName()
println(persona.age) // Llama getAge()
Propiedades vs Getters/Setters
Kotlin automáticamente convierte getters/setters de Java a propiedades:
// Java
public class User {
private String email;
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public boolean isActive() { return true; }
}
// Uso en Kotlin
val usuario = User()
usuario.email = "[email protected]" // Llama setEmail()
println(usuario.email) // Llama getEmail()
println(usuario.isActive) // Llama isActive() - nota: sin prefijo 'is'
Null Safety y Platform Types
Java no tiene null safety, así que Kotlin trata los tipos Java como "platform types":
// Java
public class JavaClass {
public String nullableString() {
return null;
}
public String nonNullString() {
return "Hola";
}
}
// Kotlin - platform types (String!)
val javaClass = JavaClass()
// Peligroso - podría lanzar NPE
val str1: String = javaClass.nullableString()
// Seguro - tipo nullable explícito
val str2: String? = javaClass.nullableString()
// Seguro con verificación de null
val str3 = javaClass.nullableString()
if (str3 != null) {
println(str3.uppercase())
}
Anotaciones @Nullable y @NotNull
Usa anotaciones Java para ayudar a Kotlin a entender la nullabilidad:
// Java con anotaciones
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class User {
@NotNull
public String getName() {
return "Alicia";
}
@Nullable
public String getEmail() {
return null;
}
}
// Kotlin ahora conoce la nullabilidad
val usuario = User()
val nombre: String = usuario.name // OK - no nullable
val email: String? = usuario.email // OK - nullable
Colecciones
Java a Kotlin:
// Java
public class JavaClass {
public List<String> getNames() {
return Arrays.asList("Alicia", "Bob", "Carlos");
}
}
// Kotlin
val javaClass = JavaClass()
val nombres = javaClass.names // List<String!>
// Convertir a tipos Kotlin
val listaKotlin: List<String> = nombres.filterNotNull()
val listaMutable: MutableList<String> = nombres.toMutableList()
Kotlin a Java:
// Kotlin
fun obtenerNumeros(): List<Int> {
return listOf(1, 2, 3, 4, 5)
}
// Java
List<Integer> numeros = MyKotlinFileKt.obtenerNumeros();
Conversiones SAM
Interfaces Java Single Abstract Method pueden ser lambda en Kotlin:
// Java
public interface OnClickListener {
void onClick(View view);
}
public class Button {
public void setOnClickListener(OnClickListener listener) {
// ...
}
}
// Kotlin - conversión SAM
val boton = Button()
boton.setOnClickListener { view ->
println("Botón clickeado")
}
// Comparar con expresión de objeto completa
boton.setOnClickListener(object : OnClickListener {
override fun onClick(view: View) {
println("Botón clickeado")
}
})
Métodos Estáticos
Java static a Kotlin:
// Java
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
// Kotlin
val resultado = MathUtils.add(5, 3) // 8
Kotlin a Java:
// Kotlin
object MathUtils {
@JvmStatic
fun add(a: Int, b: Int): Int {
return a + b
}
}
// Java
int resultado = MathUtils.add(5, 3); // Funciona con @JvmStatic
Companion Objects
// Kotlin
class MiClase {
companion object {
@JvmStatic
fun metodoEstatico() {
println("Método estático")
}
fun metodoCompanion() {
println("Método companion")
}
}
}
// Java
MiClase.metodoEstatico(); // Funciona con @JvmStatic
MiClase.Companion.metodoCompanion(); // Sin @JvmStatic
Sobrecargas
Parámetros por defecto de Kotlin a Java:
// Kotlin
@JvmOverloads
fun saludar(nombre: String, saludo: String = "Hola") {
println("$saludo, $nombre")
}
// Java - genera múltiples métodos
saludar("Alicia"); // Usa saludo por defecto
saludar("Bob", "Hola"); // Saludo personalizado
Campos
Propiedades de Kotlin a campos Java:
// Kotlin
class Usuario {
@JvmField
val nombre: String = "Alicia"
val email: String = "[email protected]"
}
// Java
Usuario usuario = new Usuario();
String nombre = usuario.nombre; // Acceso directo al campo con @JvmField
String email = usuario.getEmail(); // Getter sin @JvmField
Funciones de Extensión
Las funciones de extensión no son directamente accesibles en Java:
// Kotlin
fun String.agregarExclamacion(): String {
return "$this!"
}
// Java - debe usar método estático
String resultado = MyKotlinFileKt.agregarExclamacion("Hola");
Excepciones Checked
Kotlin no tiene excepciones checked, pero Java sí:
// Kotlin
@Throws(IOException::class)
fun leerArchivo(ruta: String): String {
throw IOException("Archivo no encontrado")
}
// Java - debe capturar o declarar
try {
String contenido = MyKotlinFileKt.leerArchivo("archivo.txt");
} catch (IOException e) {
e.printStackTrace();
}
Varargs
// Kotlin
fun suma(vararg numeros: Int): Int {
return numeros.sum()
}
// Java
int resultado = MyKotlinFileKt.suma(1, 2, 3, 4, 5);
Declaraciones de Object
// Kotlin
object Singleton {
fun hacerAlgo() {
println("Método singleton")
}
}
// Java
Singleton.INSTANCE.hacerAlgo();
Ejemplos Prácticos
Integración de Biblioteca Java:
// Usando Collections de Java
import java.util.ArrayList
import java.util.HashMap
val arrayList = ArrayList<String>()
arrayList.add("Alicia")
arrayList.add("Bob")
val hashMap = HashMap<String, Int>()
hashMap.put("Alicia", 25)
hashMap["Bob"] = 30 // Sintaxis Kotlin
Java Stream API:
import java.util.stream.Collectors
val numeros = listOf(1, 2, 3, 4, 5)
val resultado = numeros.stream()
.filter { it % 2 == 0 }
.map { it * 2 }
.collect(Collectors.toList())
println(resultado) // [4, 8]
Spring Framework:
import org.springframework.stereotype.Service
import org.springframework.beans.factory.annotation.Autowired
@Service
class ServicioUsuario @Autowired constructor(
private val repositorioUsuario: RepositorioUsuario
) {
fun encontrarUsuario(id: Long): Usuario? {
return repositorioUsuario.findById(id).orElse(null)
}
}
Buenas Prácticas
- Usa
@JvmStaticpara métodos estáticos en companion objects - Usa
@JvmOverloadspara funciones con parámetros por defecto - Usa
@JvmFieldpara exponer propiedades como campos - Usa anotaciones
@Nullabley@NotNullen Java para mejor interoperación con Kotlin - Usa
@Throwspara documentar excepciones para llamadores Java - Ten cuidado con platform types - agrega verificaciones explícitas de null
- Prefiere colecciones Kotlin pero sabe cómo trabajar con las de Java
Anotaciones Comunes
@JvmStatic- Hacer función de companion object estática@JvmOverloads- Generar métodos sobrecargados para parámetros por defecto@JvmField- Exponer propiedad como campo sin getter/setter@JvmName- Cambiar el nombre del método Java generado@Throws- Declarar excepciones checked para Java@JvmSuppressWildcards- Suprimir wildcards en tipos genéricos@JvmWildcard- Agregar wildcards a tipos genéricos
Consejos de Migración
Migración Gradual:
- Empieza con nuevas características en Kotlin
- Convierte clases de utilidad Java a Kotlin
- Convierte data classes (POJOs) a Kotlin data classes
- Migra lógica de negocio incrementalmente
- Mantén las pruebas en el lenguaje original inicialmente
- Usa características específicas de Kotlin gradualmente
Conversiones Comunes:
// Java
public final class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
// Kotlin - mucho más simple!
data class Usuario(val nombre: String, val edad: Int)
Conclusión
La interoperabilidad de Kotlin con Java es excelente, haciendo fácil adoptar Kotlin gradualmente en proyectos Java existentes. Entender los platform types y usar las anotaciones correctas asegura una integración suave entre código Kotlin y Java.
¡Esto completa el curso de Kotlin Básico! Ahora tienes una base sólida para construir aplicaciones con Kotlin.