Translate

Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas

martes, 22 de abril de 2025

Tipos Genéricos Anónimos en Scala: Wildcards y Subtipado


Antes de empezar este post tal vez sería bueno que leas este post antes :D

Scala permite definir colecciones y estructuras de datos genéricas, pero a veces no necesitamos saber con precisión qué tipo contienen, o simplemente queremos permitir varios tipos relacionados. Para esos casos existen los tipos genéricos anónimos, representados por el underscore (_).

Un tipo genérico anónimo en Scala se escribe con un guion bajo (_) en lugar de especificar un tipo concreto. Es útil cuando:

  • No necesitás conocer el tipo exacto.
  • Queremos aceptar varios subtipos o supertypos.

Veamos un ejemplo:

Lista de cualquier tipo (wildcard total)


val lista: List[_] = List(1, "hola", true)


Esto indica que lista es de algún tipo List[T], pero no importa cuál es T.


Subtipado: _ <: Tipo

Permite aceptar cualquier subtipo del tipo dado (covarianza).


class Animal

class Perro extends Animal

class Gato extends Animal


val animales: List[_ <: Animal] = List(new Perro, new Gato)


Esto significa: una lista de algo que es subtipo de Animal.


Supertyping: _ >: Tipo

Permite aceptar cualquier supertipo del tipo dado (contravarianza).


val cosas: List[_ >: Perro] = List(new Animal, new Perro)


Esto significa: una lista de algo que es supertipo de Perro.


Y ¿Por qué usar genéricos anónimos?

  • Cuando escribís funciones genéricas que pueden aceptar muchos tipos.
  • Para asegurar compatibilidad con estructuras covariantes/contravariantes.
  • Para restringir o abrir el tipo de manera controlada.

Los tipos anónimos no te permiten hacer mucho con los elementos (no podés acceder a sus métodos específicos), porque Scala no sabe exactamente qué tipo hay.


val lista: List[_] = List("hola", "chau")

// lista(0).toUpperCase()  // ERROR: no se puede garantizar el tipo


En Java existe el signo de pregunta para esto (?):


List<?> lista;

List<? extends Animal> subtipos;

List<? super Perro> supertypos;


En Scala es más limpio y expresivo:


List[_]

List[_ <: Animal]

List[_ >: Perro]


Los tipos genéricos anónimos en Scala te permiten trabajar con estructuras de datos más genéricas y flexibles, especialmente en APIs o librerías donde no se necesita o no se conoce el tipo exacto. Son ideales para mantener la seguridad de tipos sin perder generalidad.


viernes, 11 de abril de 2025

JEP 463 : Clases Implícitamente Declaradas y Métodos main de Instancia

La Propuesta de Mejora de Java (JEP) 463, titulada "Clases Implícitamente Declaradas y Métodos main de Instancia", introduce en Java 22 una funcionalidad que simplifica la escritura de programas básicos, especialmente útil para principiantes en el lenguaje.

El propósito principal de esta propuesta es permitir que los estudiantes y nuevos programadores escriban sus primeros programas en Java sin necesidad de comprender características avanzadas del lenguaje. Esto se logra permitiendo la declaración implícita de clases y la utilización de métodos main de instancia, eliminando la necesidad de especificar modificadores como public, static y el parámetro String[] args.

Las características Principales son: 

1. Clases Implícitamente Declaradas: Permiten escribir programas sin declarar explícitamente una clase. El compilador asume una clase implícita que envuelve el código proporcionado.


   void main() {

       System.out.println("Hola, mundo!");

   }


En este caso, no es necesario declarar una clase explícita; el compilador lo maneja automáticamente. 

2. Métodos main de Instancia: Permiten que el método main sea un método de instancia en lugar de un método estático. Esto facilita la introducción de conceptos orientados a objetos de manera más natural para los principiantes.


   void main() {

       System.out.println("Hola desde un método main de instancia!");

   }



Aquí, el método main no requiere ser estático, lo que simplifica la estructura del programa inicial. 

Como beneficios podemos nombrar:

  • Simplicidad: Reduce la cantidad de código ceremonial necesario para escribir programas simples, facilitando el aprendizaje inicial de Java.
  • Progresión Natural: Permite a los estudiantes comenzar con estructuras simples y luego introducir gradualmente conceptos más complejos como clases y métodos estáticos.
  • Compatibilidad: Mantiene la compatibilidad con las características existentes de Java, permitiendo una transición suave hacia programas más complejos.

Es importante destacar que esta funcionalidad se introdujo como una característica en vista previa en Java 21 bajo la JEP 445 y se reintrodujo en Java 22 con la JEP 463, incorporando mejoras basadas en la retroalimentación recibida. Como característica en vista previa, está sujeta a cambios en futuras versiones del lenguaje. 

La JEP 463 representa un avance significativo en la accesibilidad del lenguaje Java para nuevos programadores, simplificando la estructura necesaria para escribir programas básicos y facilitando una curva de aprendizaje más amigable. Al permitir la declaración implícita de clases y métodos main de instancia, Java se vuelve más accesible sin comprometer su robustez para desarrollos más complejos.

Dejo link: https://openjdk.org/jeps/463 

viernes, 4 de abril de 2025

JEP 459: Plantillas de Cadenas en Java


La propuesta de Mejora de Java (JEP) 459, titulada "Plantillas de Cadenas", introduce en Java una nueva funcionalidad que permite la creación de cadenas dinámicas de manera más sencilla y segura. Esta característica, presentada como vista previa en Java 22, complementa las literales de cadena y los bloques de texto existentes al combinar texto literal con expresiones embebidas y procesadores de plantillas para producir resultados especializados. 

Objetivos de JEP 459

  • Simplificar la escritura de programas Java: Facilita la expresión de cadenas que incluyen valores calculados en tiempo de ejecución.
  • Mejorar la legibilidad: Permite una sintaxis más clara al mezclar texto y expresiones, ya sea en una sola línea o en múltiples líneas.
  • Incrementar la seguridad: Al componer cadenas a partir de valores proporcionados por el usuario, se mejora la validación y transformación de las plantillas y sus valores embebidos.
  • Mantener flexibilidad: Permite que las bibliotecas de Java definan la sintaxis de formato utilizada en las plantillas de cadenas.
  • Simplificar el uso de APIs: Facilita la interacción con APIs que aceptan cadenas escritas en lenguajes no Java, como SQL, XML y JSON. 


Las plantillas de cadenas permiten insertar expresiones directamente dentro de cadenas de texto utilizando una sintaxis específica. A continuación, se muestra un ejemplo básico de su uso:


String nombre = "Duke";

String mensaje = STR."Hola, \{nombre}!";

System.out.println(mensaje);


En este ejemplo, `STR` es un procesador de plantillas que evalúa la expresión embebida `\{nombre}` y la reemplaza con su valor correspondiente, produciendo la salida: "Hola, Duke!". 

Java 22 incorpora varios procesadores de plantillas, entre los que se incluyen:

  • STR: Realiza interpolación de cadenas, reemplazando cada expresión embebida con su valor correspondiente convertido a cadena.
  • FMT: Similar a STR, pero acepta especificadores de formato como los definidos en java.util.Formatter, permitiendo un formateo más detallado.
  • RAW`: No procesa automáticamente la plantilla de cadena, útil para crear procesadores de plantillas personalizados.

Es fundamental tener en cuenta que las plantillas de cadenas fueron introducidas como una funcionalidad en vista previa en Java 21 y reintroducidas en Java 22 con JEP 459. Sin embargo, esta característica fue retirada en Java 23 para una revisión y rediseño adicionales, por lo que no está disponible en versiones posteriores.

Aunque actualmente esta característica está en revisión, su implementación futura promete simplificar y asegurar la construcción de cadenas en Java, facilitando la interacción con otros sistemas y mejorando la legibilidad del código.


Dejo link: https://openjdk.org/jeps/459

jueves, 3 de abril de 2025

JEP 456: Variables y Patrones Anónimos en Java



La Propuesta de Mejora de Java (JEP) 456, titulada "Variables y Patrones Anónimos", introduce en Java la capacidad de declarar variables y patrones sin nombre utilizando el carácter de subrayado (_). Esta característica, incorporada en Java 22, permite a los desarrolladores indicar explícitamente que ciertas variables o parámetros no serán utilizados, mejorando así la claridad y mantenibilidad del código. 

En situaciones donde una variable debe ser declarada pero su valor no es necesario, se puede emplear el carácter _ como nombre de la variable. Esto es útil en contextos como bucles for, bloques catch y expresiones lambda.

Veamos un ejemplo en un bucle for:


int[] numeros = {1, 2, 3, 4, 5};

int total = 0;

for (int _ : numeros) {

    total++;

}

System.out.println("Total: " + total);


En este caso, el bucle itera sobre el array numeros sin necesidad de utilizar el valor de cada elemento, incrementando simplemente el contador total.

Veamos un ejemplo en un bloque catch:


try {

    // Código que puede lanzar una IOException

} catch (IOException _) {

    System.out.println("Ocurrió un error de E/S.");

}


Aquí, se captura la excepción IOException sin necesidad de referenciarla dentro del bloque catch. 

Los patrones anónimos permiten omitir nombres y tipos en patrones de registros cuando no se requiere acceder a ciertos componentes. Esto simplifica la sintaxis y mejora la legibilidad del código al desestructurar objetos.


record Punto(int x, int y) {}

Punto punto = new Punto(5, 10);


if (punto instanceof Punto(int _, int y)) {

    System.out.println("Coordenada y: " + y);

}


En este ejemplo, se omite el nombre de la variable para la coordenada x, ya que no es necesaria, mientras que se accede directamente a la coordenada y. 

La introducción de variables y patrones anónimos en Java 22 representa un avance significativo hacia una sintaxis más concisa y expresiva, alineada con las necesidades modernas de desarrollo.

martes, 1 de abril de 2025

Novedades de java 22


¡JDK 22 ya está disponible!

JDK 22 ofrece 12 mejoras significativas. Estas mejoras abarcan mejoras en el lenguaje Java, sus API, su rendimiento y las herramientas incluidas en el JDK.

1) Mejoras del lenguaje:

  • Variables y patrones sin nombre - JEP 456: Mejora la legibilidad cuando se requieren declaraciones de variables o patrones anidados, pero no se utilizan. Ambos se indican con el guión bajo.

1.1) Vistas preview del lenguaje

  • Declaraciones antes de super(…) - JEP 447: En los constructores, permite que las declaraciones que no hacen referencia a la instancia que se está creando aparezcan antes de una invocación explícita del constructor.
  • Plantillas de cadena (Segunda versión preliminar) - JEP 459: Las plantillas de cadena complementan los literales de cadena y los bloques de texto existentes de Java al combinar el texto literal con expresiones integradas y procesadores de plantillas para producir resultados especializados.
  • Clases declaradas implícitamente y métodos principales de instancia (Segunda vista previa) - JEP 463: Los estudiantes pueden escribir sus primeros programas en Java sin necesidad de comprender las características del lenguaje diseñadas para programas grandes. En lugar de usar un dialecto independiente del lenguaje, pueden escribir declaraciones simplificadas para programas de una sola clase y luego ampliarlos sin problemas para usar funciones más avanzadas a medida que mejoran sus habilidades.


2) Bibliotecas

  • API de Funciones Externas y Memoria - JEP 454: Permite que los programas Java interoperen con código y datos fuera del entorno de ejecución de Java. Al invocar eficientemente funciones externas (es decir, código fuera de la JVM) y acceder de forma segura a la memoria externa (es decir, memoria no gestionada por la JVM), la API permite que los programas Java invoquen bibliotecas nativas y procesen datos nativos sin la fragilidad ni los riesgos de JNI.


2.1) Vistas previas de bibliotecas e incubadora

  • API de archivo de clase (Vista previa) - JEP 457: Proporciona una API estándar para analizar, generar y transformar archivos de clase Java.
  • Recolectores de streams (Vista previa) - JEP 461: Mejora la API de streams para admitir operaciones intermedias personalizadas. Esto permitirá que las canalizaciones de flujos transformen datos de maneras que no son fáciles de lograr con las operaciones intermedias integradas existentes.
  • Concurrencia estructurada (2.ª vista previa) - JEP 462: Simplifica la programación concurrente. La concurrencia estructurada trata grupos de tareas relacionadas que se ejecutan en diferentes hilos como una sola unidad de trabajo, optimizando así la gestión y cancelación de errores, mejorando la fiabilidad y la observabilidad.
  • Valores con Alcance (2.ª Vista Previa) - JEP 464: Permite compartir eficientemente datos inmutables dentro y entre subprocesos.
  • API Vector (7.ª Incubadora) - JEP 460: Una API para expresar cálculos vectoriales que se compilan de forma fiable en tiempo de ejecución para obtener instrucciones vectoriales óptimas en arquitecturas de CPU compatibles, logrando así un rendimiento superior al de cálculos escalares equivalentes. Este JEP propone reincubar la API en JDK 22, con mejoras menores en la API con respecto a JDK 21. La implementación incluye correcciones de errores y mejoras de rendimiento. Incluimos los siguientes cambios notables: Compatibilidad con el acceso vectorial con segmentos de memoria del montón respaldados por un array de cualquier tipo de elemento primitivo. Anteriormente, el acceso estaba limitado a segmentos de memoria del montón respaldados por un array de bytes.


3) Rendimiento

  • Fijación regional para G1 - JEP 423: Reduce la latencia al implementar la fijación regional en G1, de modo que no es necesario deshabilitar la recolección de elementos no utilizados durante las regiones críticas de la Interfaz Nativa de Java (JNI).


4) Herramientas

Iniciar programas de código fuente con múltiples archivos - JEP 458: Permite a los usuarios ejecutar un programa proporcionado como múltiples archivos de código fuente Java sin tener que compilarlo primero.


Java 22 continúa la evolución del lenguaje, incorporando mejoras que aumentan su expresividad, rendimiento y seguridad. Estas actualizaciones refuerzan el compromiso de la plataforma con la innovación y la adaptabilidad a las necesidades actuales de desarrollo.

En proximos post vamos a detallar las mejoras del lenguaje. 

Dejo link: https://blogs.oracle.com/java/post/the-arrival-of-java-22

lunes, 24 de marzo de 2025

Diferencias entre @Component, @Controller, @Service y @Repository en Spring Boot


Spring Boot proporciona varias anotaciones para marcar clases como beans dentro del contenedor de Spring. Entre ellas, @Component, @Controller, @Service y @Repository son las más utilizadas. Aunque todas registran un bean dentro del contexto de Spring, cada una tiene un propósito específico. A continuación, explicamos sus diferencias y usos recomendados.

@Component es la anotación más genérica y sirve para marcar cualquier clase como un bean de Spring. Cualquier clase anotada con @Component será detectada automáticamente por el escaneo de componentes de Spring y registrada en el contexto de la aplicación.


@Component

public class MiComponente {

    public void hacerAlgo() {

        System.out.println("Ejecutando lógica en MiComponente");

    }

}

Se recomienda usar @Component cuando la clase no encaje específicamente en @Controller, @Service o @Repository.


@Controller es una especialización de @Component que se utiliza en el contexto de Spring MVC para manejar solicitudes HTTP. Las clases anotadas con`@Controller se encargan de procesar peticiones web y devolver respuestas.


@Controller

public class MiControlador {

    @GetMapping("/saludo")

    public String saludo(Model model) {

        model.addAttribute("mensaje", "¡Hola desde el controlador!");

        return "saludo";

    }

}


Si el controlador necesita devolver solo datos en formato JSON o XML, se recomienda usar @RestController, que combina`@Controller y @ResponseBody.


@Service también es una especialización de @Component, pero se utiliza para indicar que una clase contiene lógica de negocio. Su uso es más semántico, ayudando a la organización y mantenimiento del código.


@Service

public class MiServicio {

    public String obtenerSaludo() {

        return "Hola desde el servicio";

    }

}


Se recomienda usar @Service para clases que encapsulen lógica de negocio y sean reutilizables en distintos lugares de la aplicación.


@Repository es otra especialización de @Component, pero está orientada a la capa de acceso a datos. Además de marcar la clase como un bean, @Repository proporciona integración con mecanismos de persistencia, como la gestión automática de excepciones relacionadas con bases de datos.


@Repository

public interface MiRepositorio extends JpaRepository<Entidad, Long> {

}

Spring usa @Repository para traducir excepciones de bases de datos en excepciones no verificadas de Spring (DataAccessException).


Cada anotación tiene su uso específico y contribuye a mantener una arquitectura limpia y organizada. Elegir la anotación adecuada ayuda a mejorar la mantenibilidad y escalabilidad de la aplicación.


martes, 11 de marzo de 2025

Clases sin Nombre y Métodos Principales sin Nombre en Java 21


Con Java 21, se introducen dos características que simplifican el código para desarrolladores: clases sin nombre y métodos main sin nombre. Estas mejoras buscan reducir la cantidad de código boilerplate en programas simples y facilitar el aprendizaje de Java para nuevos desarrolladores.

Las clases sin nombre permiten escribir código sin necesidad de definir explícitamente una clase contenedora. Esto facilita la escritura de programas pequeños o scripts sin la sobrecarga de definir clases innecesarias.

Antes de Java 21, un programa en Java requería una clase con un main:


public class MiClase {

    public static void main(String[] args) {

        System.out.println("Hola, Java 21!");

    }

}


Con las clases sin nombre, ahora puedes escribir simplemente:


System.out.println("Hola, Java 21!");


En Java 21, no es obligatorio definir explícitamente public static void main(String[] args). Ahora el código dentro del archivo fuente se ejecuta directamente.


System.out.println("Ejecutando sin un método 'main' explícito!");


¿Cómo funciona?

  • La JVM reconoce el archivo como ejecutable sin necesidad de una clase o main.
  • Útil para scripts rápidos o pruebas sencillas.
  • Sigue siendo posible definir clases si se necesita una estructura más compleja.


Las clases sin nombre y los métodos main sin nombre en Java 21 son pasos importantes para hacer el lenguaje más accesible y fácil de usar en escenarios rápidos y educativos. Si bien Java sigue siendo un lenguaje de propósito general con una fuerte orientación hacia aplicaciones empresariales, estas mejoras lo acercan más al paradigma de scripting y programación ligera, pero a mi entender nos alejan de los conceptos puros de programación orientada a objetos. 


lunes, 10 de marzo de 2025

Spring Native: La Evolución de las Aplicaciones Spring en un Mundo de Desempeño Nativo


Spring Native es un proyecto dentro del ecosistema Spring que permite compilar aplicaciones Spring Boot directamente a imágenes nativas utilizando GraalVM. Esto significa que, en lugar de ejecutar la aplicación en la JVM (Java Virtual Machine), el código se compila en un binario nativo específico de la plataforma.

¿Por qué es importante Spring Native? Como sabran, spring es muy bueno pero pero con la inyección de dependencia y el uso de proxies con lleva a un mayor uso de memoria y a un inicio de aplicación lento. Además que con docker tampoco es una ventaja ser multiplataforma. Con que funcione en docker ya esta. Todos estos problemas son resueltos con Graalvm y spring native. 

Entonces podemos nombras los siguientes beneficios:

  • Rendimiento Mejorado: Las aplicaciones nativas tienen tiempos de inicio mucho más rápidos y un consumo de memoria significativamente menor en comparación con las aplicaciones que corren sobre la JVM. Esto es crucial para aplicaciones en contenedores (como Docker) y para arquitecturas de microservicios donde la eficiencia es clave.
  • Despliegue más Eficiente: El código nativo puede simplificar la infraestructura de despliegue, ya que no depende de tener una JVM instalada. Esto puede ser muy útil en entornos donde el espacio y los recursos son limitados.
  • Menor Consumo de Memoria: Las aplicaciones nativas pueden ejecutarse con un consumo de memoria más bajo, lo que hace que sean ideales para entornos de alta escala, como la nube y la ejecución en dispositivos con recursos limitados.
  • Reducción de la Complejidad Operacional: Al generar una imagen nativa, el proceso de despliegue es más directo y menos dependiente de las complejidades de la configuración de la JVM. Esto puede mejorar la mantenibilidad y reducir los errores de configuración.
  • Mejor Compatibilidad con el Ecosistema de Contenedores: Las imágenes nativas de Spring Boot se integran de forma más eficiente con contenedores como Docker, al permitirles ser más livianas y rápidas al arrancar, lo cual es vital en escenarios donde el escalado rápido y la flexibilidad son necesarios.

Cuando usar Spring Native?: 

  • Microservicios: Dado que las aplicaciones nativas tienen tiempos de inicio rápidos y un menor uso de memoria, son perfectas para entornos de microservicios donde el arranque rápido y el escalado eficiente son esenciales.
  • Sistemas Serverless: Al ser aplicaciones de bajo consumo, pueden integrarse bien con arquitecturas serverless, donde el costo de recursos y el tiempo de ejecución son factores clave.
  • IoT y Dispositivos con Recursos Limitados: En aplicaciones que se ejecutan en dispositivos con recursos limitados, como IoT, la eficiencia de Spring Native puede ser un gran beneficio.


Desafíos y Limitaciones:

  • Compatibilidad: No todas las bibliotecas de Spring Boot son totalmente compatibles con GraalVM, y algunas características pueden requerir ajustes adicionales.
  • Tiempo de compilación: La creación de la imagen nativa puede ser lenta, lo que podría no ser ideal para todos los entornos de desarrollo.


¿Cómo Integrar Spring Native en tu Proyecto?

  • Requisitos: Tener un proyecto Spring Boot básico.
  • Configuración de GraalVM: Instalar GraalVM y configurar el entorno de desarrollo para soportar la compilación nativa.
  • Incluir dependencias nativas: Agregar la dependencia de spring-native en el pom.xml o `build.gradle`.
  • Compilación: Usar `mvn  spring-boot:build-image o ./gradlew buildNative para generar la imagen nativa.
  • Despliegue y pruebas: Probar la imagen nativa generada en el entorno de producción.


Spring Native permite a las aplicaciones Spring aprovechar los beneficios del código nativo: tiempos de arranque rápidos, menor uso de memoria y mayor eficiencia en la ejecución. A medida que las arquitecturas modernas se mueven hacia entornos de microservicios y despliegues en la nube, Spring Native se posiciona como una herramienta clave para la optimización de aplicaciones Spring.


viernes, 7 de marzo de 2025

API de Vectores en Java: Alto Rendimiento con Operaciones SIMD


Java ha introducido mejoras significativas en el rendimiento con la API de Vectores, una funcionalidad experimental desde Java 16 y evolucionada en Java 19 y 20. Esta API permite aprovechar las capacidades de SIMD (Single Instruction, Multiple Data), lo que mejora la eficiencia en cálculos intensivos, como gráficos, procesamiento de señales y machine learning.

La API de Vectores en Java permite realizar operaciones con múltiples valores en paralelo utilizando instrucciones SIMD, optimizando así el uso del hardware subyacente. Está diseñada para ejecutarse de manera más eficiente en CPU modernas con conjuntos de instrucciones avanzados como AVX y NEON.

Las características principales son:

  • Operaciones eficientes con vectores: Realiza cálculos numéricos sobre múltiples datos simultáneamente.
  • Compatibilidad con JVM y JIT: Se integra con el compilador Just-In-Time para optimizar el rendimiento.
  • Optimización automática: Se adapta a la arquitectura del procesador para aprovechar el mejor conjunto de instrucciones disponible.
  • API expresiva y segura: Usa un enfoque declarativo y sin riesgo de desbordamiento de memoria.


Veamos un ejemplo: 


import jdk.incubator.vector.FloatVector;

import jdk.incubator.vector.VectorSpecies;


public class VectorExample {

    static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;


    public static void main(String[] args) {

        float[] a = {1.0f, 2.0f, 3.0f, 4.0f};

        float[] b = {5.0f, 6.0f, 7.0f, 8.0f};

        float[] result = new float[a.length];


        var va = FloatVector.fromArray(SPECIES, a, 0);

        var vb = FloatVector.fromArray(SPECIES, b, 0);

        var vc = va.add(vb);

        vc.intoArray(result, 0);


        System.out.println("Resultado: ");

        for (float v : result) {

            System.out.print(v + " ");

        }

    }

}


  • Se define una especie de vector (SPECIES_PREFERRED) que se ajusta a la arquitectura del CPU.
  • Se cargan los datos en vectores FloatVector.
  • Se realiza una operación de suma en paralelo.
  • Se almacenan los resultados en un array de salida.


En Java 19 y 20 se continuo trabajando obteniendo estas mejoras:

  • Mayor estabilidad y optimización en JVM: Se han reducido las sobrecargas en JIT.
  • Soporte extendido para más arquitecturas: Ahora es compatible con ARM y RISC-V.
  • Nuevas operaciones matemáticas: Más funciones avanzadas como cálculos trigonométricos y logarítmicos.


La API de Vectores en Java es una gran incorporación para quienes necesitan alto rendimiento en operaciones matemáticas y científicas. Con el soporte mejorado en arquitecturas modernas y una mayor optimización en la JVM, esta API se perfila como una herramienta clave para el desarrollo de aplicaciones de alto rendimiento.

miércoles, 5 de marzo de 2025

Cómo Spring Implementa AOP


Spring AOP se basa en el Patrón Proxy y la generación dinámica de clases para aplicar aspectos sin modificar el código fuente original.

Spring AOP crea proxies para interceptar llamadas a métodos y aplicar lógica adicional. Dependiendo de la estructura de la clase objetivo, Spring elige entre dos enfoques:

JDK Dynamic Proxies: Si la clase implementa una interfaz, Spring usa java.lang.reflect.Proxy para generar un proxy en tiempo de ejecución.

CGLIB (Code Generation Library): Si la clase no implementa interfaces, se genera una subclase dinámica con CGLIB.


Veamos un ejemplo de un proxy con JDK:


import java.lang.reflect.*;


interface Servicio {

    void ejecutar();

}


class ServicioImpl implements Servicio {

    public void ejecutar() {

        System.out.println("Ejecutando servicio...");

    }

}


class ProxyHandler implements InvocationHandler {

    private final Object target;

    public ProxyHandler(Object target) {

        this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("[AOP] Antes de ejecutar");

        Object result = method.invoke(target, args);

        System.out.println("[AOP] Después de ejecutar");

        return result;

    }

}


public class Main {

    public static void main(String[] args) {

        Servicio servicio = (Servicio) Proxy.newProxyInstance(

            Servicio.class.getClassLoader(),

            new Class[]{Servicio.class},

            new ProxyHandler(new ServicioImpl()));

        servicio.ejecutar();

    }

}


Si la clase no implementa interfaces, Spring usa CGLIB para generar una subclase dinámica.


import net.sf.cglib.proxy.*;


class Servicio {

    public void ejecutar() {

        System.out.println("Ejecutando servicio...");

    }

}


class Interceptor implements MethodInterceptor {

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        System.out.println("[AOP] Antes de ejecutar");

        Object result = proxy.invokeSuper(obj, args);

        System.out.println("[AOP] Después de ejecutar");

        return result;

    }

}


public class Main {

    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(Servicio.class);

        enhancer.setCallback(new Interceptor());

        Servicio proxy = (Servicio) enhancer.create();

        proxy.ejecutar();

    }

}


Spring AOP aplica aspectos mediante proxies dinámicos. Si la clase tiene una interfaz, usa JDK Dynamic Proxies; si no, usa CGLIB para generar una subclase dinámica. Esto permite agregar lógica sin modificar el código fuente ni el bytecode.



viernes, 28 de febrero de 2025

API de Servidor Web Simple en Java



A partir de Java 18, se introdujo una API de Servidor Web Simple que permite crear servidores HTTP ligeros sin necesidad de dependencias externas. Esta funcionalidad es ideal para pruebas rápidas, prototipos y aplicaciones livianas.

Como características principales podemos nombrar:

  • Servidor HTTP embebido sin necesidad de librerías adicionales.
  • Manejo de solicitudes GET y POST.
  • Facilidad de uso con pocas líneas de código.
  • Soporte para configuración de rutas y respuestas personalizadas.


Veamos un hola mundo : 


import com.sun.net.httpserver.HttpServer;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;

import java.io.OutputStream;

import java.net.InetSocketAddress;


public class SimpleWebServer {

    public static void main(String[] args) throws IOException {

        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        server.createContext("/hello", new HelloHandler());

        server.setExecutor(null);

        server.start();

        System.out.println("Servidor iniciado en http://localhost:8080/");

    }


    static class HelloHandler implements HttpHandler {

        @Override

        public void handle(HttpExchange exchange) throws IOException {

            String response = "¡Hola, mundo!";

            exchange.sendResponseHeaders(200, response.length());

            OutputStream os = exchange.getResponseBody();

            os.write(response.getBytes());

            os.close();

        }

    }

}


La API de Servidor Web Simple en Java es una herramienta útil para desarrollar servidores HTTP de forma rápida y sencilla. Aunque tiene limitaciones en cuanto a escalabilidad y características avanzadas, es perfecta para pruebas y aplicaciones pequeñas sin necesidad de frameworks externos.


miércoles, 26 de febrero de 2025

API de Acceso a Memoria Externa en Java


La API de Acceso a Memoria Externa (Foreign Function & Memory API) en Java es una funcionalidad introducida para interactuar de manera segura y eficiente con memoria fuera del heap de Java. Esta API ha evolucionado en versiones recientes y permite mejorar la interoperabilidad con código nativo sin necesidad de JNI (Java Native Interface).

Esta API proporciona una forma segura y estructurada de acceder a la memoria fuera del control del recolector de basura, lo que permite mejorar el rendimiento en aplicaciones que requieren interacción con bibliotecas nativas o manipulación intensiva de datos.

Veamos un ejemplo básico de cómo asignar y manipular memoria externa en Java utilizando la API:


import java.lang.foreign.*;

import java.lang.invoke.VarHandle;


public class ForeignMemoryExample {

    public static void main(String[] args) {

        try (MemorySegment segment = MemorySegment.allocateNative(100)) {

            VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

            intHandle.set(segment, 0, 42);

            System.out.println("Valor almacenado: " + intHandle.get(segment, 0));

        }

    }

}


Casos de uso:

  •  Interoperabilidad con bibliotecas en C: Facilita la comunicación con código nativo sin JNI.
  •  Manipulación de grandes volúmenes de datos: Ideal para manejar grandes bloques de memoria sin afectar el GC.
  •  Optimización en aplicaciones de alto rendimiento: Como motores de bases de datos y procesamiento de señales.


La API de Acceso a Memoria Externa en Java representa un avance importante en la forma en que interactuamos con memoria nativa. Al ofrecer una interfaz más segura y eficiente, facilita el desarrollo de aplicaciones de alto rendimiento sin comprometer la seguridad y estabilidad del código Java.


lunes, 24 de febrero de 2025

Pattern Matching en Java


La coincidencia de patrones (Pattern Matching) en switch es una característica introducida en Java 17 como una mejora en la sintaxis del switch, permitiendo evaluar tipos de manera más sencilla y concisa. Esta funcionalidad facilita la escritura de código más limpio y seguro al eliminar la necesidad de casting manual.

Veamos un ejemplo, antes de java 17 haciamos:

 

static void procesar(Object obj) {

    switch (obj.getClass().getSimpleName()) {

        case "String":

            String s = (String) obj;

            System.out.println("Es una cadena: " + s.toUpperCase());

            break;

        case "Integer":

            Integer i = (Integer) obj;

            System.out.println("Es un número: " + (i * 2));

            break;

        default:

            System.out.println("Tipo no soportado");

    }

}


Y en Java 17: 


static void procesar(Object obj) {

    switch (obj) {

        case String s -> System.out.println("Es una cadena: " + s.toUpperCase());

        case Integer i -> System.out.println("Es un número: " + (i * 2));

        default -> System.out.println("Tipo no soportado");

    }

}


Esta disponible a partir de Java 17 en vista previa y consolidado en versiones posteriores. Algo a tener en cuanta es que solo se puede utilizar en switch con expresiones y no con estructuras más antiguas.

El pattern matching en switch es una mejora significativa que permite un código más limpio y expresivo. Reduce el uso de casts y mejora la legibilidad del código, haciendo que la programación en Java sea más moderna y eficiente.


sábado, 22 de febrero de 2025

Clases Selladas en Java


Las clases selladas en Java, introducidas en Java 15 como una característica en vista previa y estandarizadas en Java 17, permiten restringir qué clases pueden heredar de una clase base, mejorando la seguridad y el diseño del código.

Las clases selladas (sealed) establecen un conjunto específico de clases que pueden extender o implementar una clase base, evitando la herencia no controlada.


public sealed class Figura permits Circulo, Rectangulo {}


public final class Circulo extends Figura {}

public final class Rectangulo extends Figura {}


Aquí, Figura es una clase sellada, lo que significa que solo Circulo y Rectangulo pueden heredar de ella.

Las clases que extienden una clase sellada deben usar una de las siguientes opciones:

  • final: No permite más subclases.
  • sealed: Permite definir otro conjunto restringido de subclases.
  • non-sealed: Permite herencia sin restricciones.


Ejemplo con diferentes modificaciones:


public sealed class Figura permits Circulo, Poligono {}


public final class Circulo extends Figura {}

public sealed class Poligono extends Figura permits Triangulo {}

public non-sealed class Triangulo extends Poligono {}


Las clases selladas en Java proporcionan un control más preciso sobre la herencia, facilitando la definición de modelos de dominio más seguros y expresivos. Son especialmente útiles cuando se combinan con switch y pattern matching para estructurar mejor el código.

lunes, 17 de febrero de 2025

Pattern matching para instanceof en Java


Pattern matching para instanceof es una característica introducida en Java 14 como una mejora para escribir código más conciso y seguro al realizar comprobaciones de tipo y conversiones.

Antes de Java 14, verificar si un objeto era de cierto tipo y luego convertirlo requería código repetitivo:


if (obj instanceof String) {

    String str = (String) obj;

    System.out.println("Longitud: " + str.length());

}


La coincidencia de patrones para `instanceof` elimina la necesidad del casting explícito, reduciendo la repetición:


if (obj instanceof String str) {

    System.out.println("Longitud: " + str.length());

}


La coincidencia de patrones se puede usar en estructuras más complejas como `else if`:


public void imprimir(Object obj) {

    if (obj instanceof String str) {

        System.out.println("Es un String: " + str);

    } else if (obj instanceof Integer num) {

        System.out.println("Es un Integer: " + num);

    } else {

        System.out.println("Tipo desconocido");

    }

}


La variable resultante solo está disponible dentro del bloque if, lo que evita posibles accesos indebidos:


if (obj instanceof String str) {

    System.out.println(str.length()); // Válido

}

System.out.println(str); // Error: No está definido fuera del if


La coincidencia de patrones para instanceof simplifica la escritura de código en Java, haciéndolo más expresivo y seguro. Esta funcionalidad es el primer paso hacia mejoras más avanzadas en la manipulación de tipos en el lenguaje.


Lombok @Builder: Simplificando la Creación de Objetos en Java


En el blog nunca he hablado de lombok, porque no me gusta que exista un framework que haga lo que tendria que tener el lenguaje que programamos. Pero esta funcionalidad de lombok no la conocia y me esta buena. 

La creación de objetos con múltiples atributos puede volverse tediosa y propensa a errores si se usa el enfoque tradicional de constructores o setters. Lombok ofrece la anotación @Builder, que es una solución elegante que permite generar automáticamente un patrón de construcción de objetos de manera fluida y legible.

Lombok es una librería que reduce el código repetitivo en Java mediante anotaciones que generan automáticamente métodos como getter, setter, equals, hashCode y toString. Una de sus anotaciones más útiles es @Builder, que facilita la creación de objetos sin necesidad de escribir constructores manualmente.

Para agregar Lombok, si usas Maven, agrega la siguiente dependencia:


<dependency>

    <groupId>org.projectlombok</groupId>

    <artifactId>lombok</artifactId>

    <version>1.18.26</version>

    <scope>provided</scope>

</dependency>


Si usas Gradle:


dependencies {

    compileOnly 'org.projectlombok:lombok:1.18.26'

    annotationProcessor 'org.projectlombok:lombok:1.18.26'

}


Veamos un ejemplo de una clase que tiene un builder: 


import lombok.Builder;

import lombok.Getter;

import lombok.ToString;


@Getter

@ToString

@Builder

public class Usuario {

    private String nombre;

    private String email;

    private int edad;

}

public class Main {

    public static void main(String[] args) {

        Usuario usuario = Usuario.builder()

                .nombre("Juan Pérez")

                .email("juan.perez@example.com")

                .edad(30)

                .build();

        

        System.out.println(usuario);

    }

}


Se puede personalizar el constructor y el nombre del método de construcción con @Builder, veamos un ejemplo :


@Builder(builderMethodName = "nuevoUsuario")

public class Usuario {

    private String nombre;

    private String email;

    private int edad;

}


Uso:


Usuario usuario = Usuario.nuevoUsuario()

        .nombre("Ana López")

        .email("ana.lopez@example.com")

        .edad(28)

        .build();


El uso de @Builder en Lombok simplifica la creación de objetos en Java, haciendo que el código sea más conciso, flexible y fácil de mantener. Esta anotación es especialmente útil cuando se manejan objetos con múltiples atributos opcionales, evitando la necesidad de escribir múltiples constructores sobrecargados.


Dejo link:

https://www.baeldung.com/lombok-builder

sábado, 15 de febrero de 2025

Record en Java


Con la introducción de los records en Java 14 como una feature en vista previa y su estandarización en Java 16, ahora es más fácil crear clases inmutables de manera concisa y sin la sobrecarga de escribir código repetitivo.

Un record es una clase especial diseñada para almacenar datos de manera inmutable. Se encarga automáticamente de generar los métodos equals(), hashCode(), toString() y los accesores (`getters`) sin necesidad de escribir código adicional.

Veamos un ejemplo: 


public record Usuario(String nombre, String email, int edad) {}


La declaración anterior equivale a escribir lo siguiente en una clase tradicional:


public final class Usuario {

    private final String nombre;

    private final String email;

    private final int edad;

    

    public Usuario(String nombre, String email, int edad) {

        this.nombre = nombre;

        this.email = email;

        this.edad = edad;

    }

    

    public String nombre() { return nombre; }

    public String email() { return email; }

    public int edad() { return edad; }

    

    @Override

    public boolean equals(Object o) { /* Implementación automática */ }

    

    @Override

    public int hashCode() { /* Implementación automática */ }

    

    @Override

    public String toString() { /* Implementación automática */ }

}


Veamos como usar un record: 


public class Main {

    public static void main(String[] args) {

        Usuario usuario = new Usuario("Juan Pérez", "juan.perez@example.com", 30);       

        System.out.println(usuario);

    }

}


Salida:

Usuario[nombre=Juan Pérez, email=juan.perez@example.com, edad=30]


Se puede agregar métodos adicionales si es necesario:


public record Usuario(String nombre, String email, int edad) {

    public boolean esMayorDeEdad() {

        return edad >= 18;

    }

}


Los record en Java son una herramienta poderosa para definir estructuras de datos inmutables de forma concisa y eficiente. Gracias a ellos, el código es más limpio, menos propenso a errores y más fácil de mantener.


jueves, 13 de febrero de 2025

AssertJ


En el mundo de las pruebas unitarias en Java, AssertJ se ha consolidado como una de las herramientas más poderosas y expresivas para realizar aserciones. Este framework proporciona una API fluida y altamente legible que facilita la validación de resultados en las pruebas, mejorando la mantenibilidad y claridad del código.

AssertJ es una librería de aserciones para Java con una API expresiva y encadenable. Entre sus principales características se encuentran:

  • Sintaxis fluida y encadenada.
  • Mejor manejo de colecciones y excepciones.
  • Comparaciones avanzadas con objetos y fechas.
  • Integración con JUnit y TestNG.

Para utilizar AssertJ en un proyecto Maven, agrega la siguiente dependencia:


<dependency>

    <groupId>org.assertj</groupId>

    <artifactId>assertj-core</artifactId>

    <version>3.24.2</version>

    <scope>test</scope>

</dependency>



Si usas Gradle:

testImplementation 'org.assertj:assertj-core:3.24.2'


Veamos  un ejemplo de cómo realizar aserciones básicas con AssertJ:



import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.Test;


class AssertJExampleTest {

    @Test

    void testBasicAssertions() {

        String mensaje = "Hola AssertJ";

        assertThat(mensaje)

            .isNotNull()

            .startsWith("Hola")

            .endsWith("AssertJ")

            .contains("AssertJ");

    }

}


Otro ejemplo: 

@Test

void testNumbers() {

    int resultado = 10;

    assertThat(resultado)

        .isPositive()

        .isGreaterThan(5)

        .isLessThanOrEqualTo(10);

}


@Test

void testListAssertions() {

    List<String> nombres = List.of("Juan", "Maria", "Carlos");

    assertThat(nombres)

        .hasSize(3)

        .contains("Maria")

        .doesNotContain("Pedro")

        .containsExactly("Juan", "Maria", "Carlos");

}


Veamos Aserciones con Excepciones:


@Test

void testException() {

    assertThatThrownBy(() -> {

        throw new IllegalArgumentException("Error de prueba");

    }).isInstanceOf(IllegalArgumentException.class)

      .hasMessageContaining("Error");

}


AssertJ es una excelente opción para mejorar las pruebas unitarias en Java, ofreciendo una API intuitiva y potente que facilita la validación de resultados. Su uso ayuda a escribir pruebas más claras y mantenibles, mejorando la calidad del código.


Dejo link: https://www.baeldung.com/introduction-to-assertj

lunes, 10 de febrero de 2025

Bloques de Texto en Java


Con la introducción de los Bloques de Texto en Java 13 y su estandarización en Java 15, la manipulación de cadenas multilínea se ha vuelto mucho más sencilla y legible. Esta característica permite definir textos sin necesidad de escapar caracteres especiales o concatenar múltiples líneas, lo que mejora la claridad del código.

Los bloques de texto son literales de cadena que pueden abarcar múltiples líneas, definidos usando tres comillas dobles ("""). Se utilizan para escribir fragmentos de texto extensos sin necesidad de concatenaciones o caracteres de escape innecesarios.

Antes de los bloques de texto, una cadena multilínea en Java debía escribirse así:

String json = "{\n" +

              "    \"nombre\": \"Juan\",\n" +

              "    \"edad\": 25\n" +

              "}";

Con los bloques de texto, el mismo código se simplifica de la siguiente manera:


String json = """

    {

        "nombre": "Juan",

        "edad": 25

    }

    """;


Los bloques de texto son especialmente útiles en consultas SQL y plantillas HTML. Por ejemplo:


String query = """

    SELECT * FROM usuarios

    WHERE edad > 18

    ORDER BY nombre;

    """;


Otro ejemplo con HTML:


String html = """

    <html>

        <body>

            <h1>Bienvenido</h1>

        </body>

    </html>

    """;

Java mantiene la indentación de los bloques de texto, pero puedes usar `stripIndent()` para eliminar espacios innecesarios.

Puedes utilizar formatted() para reemplazo de valores dinámicos dentro del bloque.

Los bloques de texto en Java ofrecen una solución elegante para manejar cadenas multilínea de forma clara y eficiente. Su uso simplifica la lectura y escritura de código, mejorando la productividad de los desarrolladores.


martes, 4 de febrero de 2025

Haciendo fácil el calculo de hashing en archivoa con java.security.MessageDigest


Java 12 introdujo una API que facilita el cálculo de resúmenes de archivos (hashing) de manera eficiente y sencilla mediante la clase java.security.MessageDigest. Esta API permite generar hashes de archivos utilizando algoritmos como SHA-256 o MD5 sin necesidad de manejar manualmente la lectura de bytes y el procesamiento del hash.

Un hash de archivo es un valor único derivado de su contenido, generado por una función hash criptográfica. Es ampliamente utilizado para:

  • Verificar la integridad de archivos.
  • Comparar grandes volúmenes de datos de manera eficiente.
  • Validar la autenticidad de descargas.

Java 12 simplificó el proceso de generación de hash de archivos mediante el uso de `MessageDigest` junto con `Files.newInputStream`.

Veamos un ejemplo de uso con SHA-256:


import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Path;

import java.security.DigestInputStream;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.util.HexFormat;


public class FileHashingExample {

    public static void main(String[] args) throws IOException, NoSuchAlgorithmException {

        Path filePath = Path.of("archivo.txt");

        

        String hash = calculateFileHash(filePath, "SHA-256");

        System.out.println("Hash SHA-256: " + hash);

    }

    

    public static String calculateFileHash(Path path, String algorithm) throws IOException, NoSuchAlgorithmException {

        MessageDigest digest = MessageDigest.getInstance(algorithm);

        

        try (DigestInputStream dis = new DigestInputStream(Files.newInputStream(path), digest)) {

            while (dis.read() != -1) { } // Leer completamente el archivo

        }

        

        byte[] hashBytes = digest.digest();

        return HexFormat.of().formatHex(hashBytes);

    }

}


Otra mejora en Java 12 es el método Files.mismatch, que permite comparar dos archivos y determinar la primera posición donde difieren. Esto es útil para verificaciones de integridad.


import java.nio.file.Files;

import java.nio.file.Path;

import java.io.IOException;


public class FileComparisonExample {

    public static void main(String[] args) throws IOException {

        Path file1 = Path.of("archivo1.txt");

        Path file2 = Path.of("archivo2.txt");

        

        long mismatch = Files.mismatch(file1, file2);

        

        if (mismatch == -1) {

            System.out.println("Los archivos son idénticos.");

        } else {

            System.out.println("Los archivos difieren en la posición: " + mismatch);

        }

    }

}


Esta API facilita el cálculo de hashes y la comparación de archivos, mejorando la eficiencia y seguridad del procesamiento de datos. Estas mejoras hacen que Java sea una opción aún más atractiva para tareas de integridad de archivos y validación criptográfica.