Java

Relaciona las abstracciones presentadas en Kotlin con sus mecanismos idiomáticos en Java 25 LTS. La guía compara responsabilidades de diseño y garantías, no solo sintaxis.

Funciones y abstracciones de comportamiento

Tratar funciones como valores es la base para leer y escribir el resto de las comparaciones: sin esto, los mecanismos posteriores parecen sintaxis arbitraria.

Equivalencia desde Kotlin

Comportamiento como valor

Cercana

Java no tiene funciones de nivel superior. Para este caso se usa un método estático y una lambda cuyo tipo destino es una interfaz funcional. @FunctionalInterface no es lo que hace que una interfaz sea funcional (basta con un único método abstracto); la anotación solo hace que el compilador verifique esa propiedad.

Referencia Kotlin: Declarar una operación reutilizable y recibir otra operación como valor.

Mecanismo
Método static + interfaz funcional. Java representa el comportamiento recibido mediante una interfaz funcional, como IntUnaryOperator.
Idea conservada
El algoritmo depende de un comportamiento, no de su implementación. applyTwice puede recibir distintas lambdas o referencias a método sin conocer cómo realizan la transformación.
Riesgo de diseño
Una lambda necesita un tipo destino. El contexto debe indicar qué interfaz funcional representa la lambda.
Convención del curso
Usa una interfaz de java.util.function cuando exprese correctamente el contrato. Declara una interfaz propia solo cuando el nombre del dominio aporte significado, por ejemplo Objective<C>.

Diferencias relevantes

  • El método debe pertenecer a una clase o interfaz.
  • La lambda adquiere su tipo desde IntUnaryOperator, no desde una sintaxis de tipo de función propia.
  • Una operación que recibe comportamiento (orden superior) usa la misma idea con otra forma de interfaz funcional, como Predicate<T> para una condición en vez de una transformación.

La misma transformación mediante una interfaz funcional estándar.

Kotlin
fun applyTwice(
    value: Int,
    transform: (Int) -> Int,
): Int = transform(transform(value))

val result = applyTwice(3) { it + 1 } // 5
Java
package atom.functions;

import java.util.function.IntUnaryOperator;

final class ApplyTwice {
    private ApplyTwice() {}

    static int applyTwice(
        int value,
        IntUnaryOperator transform
    ) {
        int firstResult = transform.applyAsInt(value);
        return transform.applyAsInt(firstResult);
    }
}

El tipo destino IntUnaryOperator indica qué interfaz funcional representa la lambda.

IntUnaryOperator increment =
    value -> value + 1; // Válido

var increment =
    value -> value + 1; // No compila

Otra forma de interfaz funcional: un Predicate<T> recibido para filtrar una lista.

package atom.functions;

import java.util.List;
import java.util.function.Predicate;

final class Keep {
    private Keep() {}

    static List<Integer> keep(List<Integer> xs, Predicate<Integer> p) {
        return xs.stream().filter(p).toList();
    }
}

Una interfaz propia aporta vocabulario cuando el contrato pertenece al dominio.

@FunctionalInterface
interface Objective<C> {
    double evaluate(C candidate);
}

Fuentes

Equivalencia desde Kotlin

Parámetros variádicos

Cercana

Un parámetro variádico recibe cero o más argumentos homogéneos. Java los representa como un arreglo; la sintaxis double... rest solo mejora la llamada.

Referencia Kotlin: Una función recibe vararg values: Int.

Mecanismo
parámetros int...
Riesgo de diseño
Usar varargs para ocultar una colección que el dominio almacena, reutiliza o comparte entre componentes.
Recomendación
Usa varargs para llamadas breves y legibles; modela el conjunto principal de datos con List<T>.

Diferencias relevantes

  • El parámetro variádico debe ser el último de la lista.
  • Kotlin exige *values para expandir un arreglo; Java permite pasar el arreglo directamente.

Suma una cantidad variable de enteros.

static int sum(int... values) { return Arrays.stream(values).sum(); }

Fuentes

Equivalencia desde Kotlin

Interfaces

Cercana

Una interfaz Java define un contrato nominal: una clase lo satisface al declararlo explícitamente. El consumidor puede depender de esa capacidad y no de la representación concreta.

Referencia Kotlin: Una interfaz declara comportamiento sin fijar una representación.

Mecanismo
interface nominal
Riesgo de diseño
Extraer una interfaz para cada clase concreta sin una necesidad de sustitución, extensión o aislamiento en pruebas.
Recomendación
Extrae una interfaz solo cuando exista una frontera de comportamiento estable.

Diferencias relevantes

  • La varianza se expresa en los puntos de uso, no en Sampler<C>.
  • Los métodos de interfaz son públicos por contrato; default, static y privados deben mantenerse subordinados a una responsabilidad principal.

Permite reemplazar un proveedor de resultados por su contrato.

interface Score { int value(); }

Fuentes