Translate

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

jueves, 11 de julio de 2024

Como podemos manejar las referencias nulas?


El error más frecuente en Java es NullPointerException y me imagino que en otros lenguajes alguno similar...  Para abordar esto, se han introducido estructuras y operadores que ayudan a manejar la ausencia de valores de manera más segura y explícita. 

Por ejemplo en Java se introdujo la clase `Optional` en la versión 8 para manejar valores potencialmente nulos de una manera más segura. `Optional` es un contenedor que puede o no contener un valor no nulo.

import java.util.Optional;


public class OptionalExample {

    public static void main(String[] args) {

        Optional<String> optional = Optional.of("Hello, World!");

        

        // Verificar si hay un valor presente

        if (optional.isPresent()) {

            System.out.println(optional.get());

        }

        

        // Uso del método ifPresent

        optional.ifPresent(System.out::println);

        

        // Proveer un valor predeterminado

        String value = optional.orElse("Default Value");

        System.out.println(value);

        

        // Proveer un valor predeterminado usando un Supplier

        value = optional.orElseGet(() -> "Default Value from Supplier");

        System.out.println(value);

    }

}


Scala utiliza la clase `Option` para representar un valor opcional. `Option` tiene dos subclases: `Some` y `None`, lo que proporciona una forma elegante y funcional de manejar valores que pueden estar ausentes. Esta idea es similar a la monada `Maybe` en Haskell.


object OptionExample extends App {

  val someValue: Option[String] = Some("Hello, World!")

  val noneValue: Option[String] = None


  // Uso de getOrElse

  println(someValue.getOrElse("Default Value"))

  println(noneValue.getOrElse("Default Value"))


  // Uso del patrón de coincidencia (Pattern Matching)

  someValue match {

    case Some(value) => println(value)

    case None => println("No value")

  }


  noneValue match {

    case Some(value) => println(value)

    case None => println("No value")

  }

}


Scala "copio" esta forma de Haskell. Haskell utiliza el tipo de datos `Maybe` para manejar valores opcionales `Maybe` puede ser `Just` un valor o `Nothing`.


main :: IO ()

main = do

    let someValue = Just "Hello, World!"

    let noneValue = Nothing


    -- Uso de fromMaybe

    putStrLn (fromMaybe "Default Value" someValue)

    putStrLn (fromMaybe "Default Value" noneValue)


    -- Uso del patrón de coincidencia (Pattern Matching)

    case someValue of

        Just value -> putStrLn value

        Nothing -> putStrLn "No value"


    case noneValue of

        Just value -> putStrLn value

        Nothing -> putStrLn "No value"


Kotlin es similar a Scala en muchos aspectos pero no en este. Kotlin introduce el operador `?` para facilitar la gestión de valores nulos. Este operador se utiliza para declarar tipos de datos que pueden ser nulos y para realizar operaciones seguras contra nulos.


fun main() {

    var nullableString: String? = "Hello, World!"


    // Uso del operador ?. para llamadas seguras

    println(nullableString?.length)


    // Uso del operador ?: para proporcionar un valor predeterminado

    val length = nullableString?.length ?: 0

    println(length)


    nullableString = null


    // Uso de let para ejecutar código solo si el valor no es nulo

    nullableString?.let {

        println(it)

    }

}


C# ha incluido varias características para manejar valores nulos, como el operador `?`, que facilita el manejo seguro de tipos que pueden ser nulos.


using System;


class Program

{

    static void Main()

    {

        string? nullableString = "Hello, World!";

        

        // Uso del operador ?. para llamadas seguras

        Console.WriteLine(nullableString?.Length);


        // Uso del operador ?? para proporcionar un valor predeterminado

        int length = nullableString?.Length ?? 0;

        Console.WriteLine(length);


        nullableString = null;


        // Uso de pattern matching para verificar nulos

        if (nullableString is string nonNullString)

        {

            Console.WriteLine(nonNullString);

        }

    }

}


Rust maneja la ausencia de valores y los errores de una manera robusta utilizando los tipos `Option` y `Result`. `Option` puede ser `Some` o `None`, mientras que `Result` puede ser `Ok` o `Err`.


fn main() {

    let some_value: Option<String> = Some("Hello, World!".to_string());

    let none_value: Option<String> = None;


    // Uso de unwrap_or

    println!("{}", some_value.unwrap_or("Default Value".to_string()));

    println!("{}", none_value.unwrap_or("Default Value".to_string()));


    // Uso del patrón de coincidencia (Pattern Matching)

    match some_value {

        Some(value) => println!("{}", value),

        None => println!("No value"),

    }


    match none_value {

        Some(value) => println!("{}", value),

        None => println!("No value"),

    }

}


Go no tiene un tipo de datos específico para manejar valores opcionales, pero utiliza la convención de retornar múltiples valores, incluyendo un valor y un `error`. Que la verdad no me gusta, te pasas preguntando todo el tiempo si hay error o si los valores son nulos. 


package main


import (

    "errors"

    "fmt"

)


func getValue() (string, error) {

    return "Hello, World!", nil

}


func getNullableValue() (string, error) {

    return "", errors.New("no value")

}


func main() {

    value, err := getValue()

    if err != nil {

        fmt.Println("Error:", err)

    } else {

        fmt.Println("Value:", value)

    }


    nullableValue, err := getNullableValue()

    if err != nil {

        fmt.Println("Error:", err)

    } else {

        fmt.Println("Value:", nullableValue)

    }

}


Python utiliza la palabra clave `None` para representar la ausencia de valor. Aunque no tiene una estructura específica como `Optional`, los desarrolladores pueden utilizar condicionales y manejo de excepciones.


def get_value():

    return "Hello, World!"


def get_nullable_value():

    return None


value = get_value()

nullable_value = get_nullable_value()


if value is not None:

    print(value)

else:

    print("Default Value")


if nullable_value is not None:

    print(nullable_value)

else:

    print("Default Value")


Ruby utiliza `nil` para representar la ausencia de valor. Al igual que en Python, no tiene una estructura específica para valores opcionales, pero proporciona métodos para manejar `nil`.


value = "Hello, World!"

nullable_value = nil


# Uso del operador ||

puts value || "Default Value"

puts nullable_value || "Default Value"


# Uso de condicionales

puts value.nil? ? "Default Value" : value

puts nullable_value.nil? ? "Default Value" : nullable_value


C++ utiliza punteros inteligentes (`smart pointers`) para gestionar la memoria y prevenir errores relacionados con punteros nulos. Los punteros inteligentes, como `std::unique_ptr` y `std::shared_ptr`, se encargan de la gestión automática de la memoria.


#include <iostream>

#include <memory>


int main() {

    std::unique_ptr<int> uniquePtr(new int(42));

    if (uniquePtr) {

        std::cout << *uniquePtr << std::endl;

    }


    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);

    if (sharedPtr) {

        std::cout << *sharedPtr << std::endl;

    }


    // Uso de weak_ptr para evitar ciclos de referencia

    std::weak_ptr<int> weakPtr = sharedPtr;

    if (auto lockedPtr = weakPtr.lock()) {

        std::cout << *lockedPtr << std::endl;

    }


    return 0;

}


TypeScript, un superconjunto de JavaScript, permite tipos opcionales y tiene un soporte robusto para manejar valores `null` y `undefined`.


let nullableString: string | null = "Hello, World!";


// Uso del operador ? para llamadas seguras

console.log(nullableString?.length ?? 0);


// Uso de if para asegurar valores no nulos

if (nullableString !== null) {

    console.log(nullableString);

}


TypeScript utiliza tipos opcionales para manejar valores que pueden ser `null` o `undefined`, proporcionando un enfoque seguro para evitar errores comunes relacionados con valores nulos. El operador `?.` permite realizar llamadas seguras, y el operador `??` proporciona valores predeterminados en caso de valores `null` o `undefined`.

En fin, aunque la gestión de valores nulos varía entre lenguajes, la idea subyacente es la misma: proporcionar mecanismos más seguros y expresivos para manejar la ausencia de valores. Ya sea mediante clases contenedoras como `Optional` en Java y `Option` en Scala, tipos de datos como `Maybe` en Haskell, operadores específicos como `?` en Kotlin y C#, punteros inteligentes en C++, o enfoques específicos en Rust, Go, Python y Ruby, estos enfoques ayudan a reducir los errores y a escribir un código más robusto y mantenible.


miércoles, 27 de septiembre de 2023

Libros gratuitos

 Me llegaron ests libros de web code geeks :

Get Schooled By Web Code Geeks

Download FREE IT Guides!

 

Starting with Kotlin Cheatsheet

Welcome to the Kotlin Cheatsheet! This document aims to provide you with a quick reference guide to Kotlin programming language. Whether you are a beginner getting started with Kotlin or...

 
 

Querying Graphs with Neo4j Cheatsheet

This cheatsheet is your guide to effectively querying graphs using Neo4j. Whether you’re a seasoned database professional looking to expand your skills or a curious enthusiast eager to...

 
 

Getting Started with GraphQL Cheatsheet

In this cheatsheet, we will embark on a journey to explore the core principles of GraphQL and its ecosystem. We'll cover topics such as schema design, querying and mutation operations,...

 
 

Starting with Windows PowerShell Cheatsheet

This cheatsheet provides an overview of the most commonly used PowerShell commands, grouped by category. Whether you’re new to PowerShell or an experienced user, this cheatsheet will...

 
 

Core Python Cheatsheet

This cheatsheet is intended to serve as a quick reference guide for Python programming. It covers some of the most commonly used syntax and features of the language, including data types,...

 
 

Starting with Docker Cheatsheet

This guide aims to provide an overview of the key concepts and features of Docker, along with practical examples and tips for using it effectively. It covers topics such as Docker...

 

domingo, 25 de septiembre de 2022

Sealed classes de Kotlin: definición de jerarquías de clases restringidas


Veamos un ejemplo para entender mejor para que utilizar seald classess. La superclase Expr tiene dos subclases: Num, que representa un número; y Sum, que representa la suma de dos expresiones. Es conveniente manejar todas las subclases posibles en una expresión when. Pero tenemos que escribir el  else para especificar qué debería suceder si no tenemos coincidencias:


interface Expr

    class Num(val value: Int) : Expr

    class Sum(val left: Expr, val right: Expr) : Expr


fun eval(e: Expr): Int =

    when (e) {

        is Num -> e.value

        is Sum -> eval(e.right) + eval(e.left)

        else ->

            throw IllegalArgumentException("Unknown expression")

}


Cuando evalúas una expresión usando la construcción when, el compilador de Kotlin te obliga a especificar todas las opciones, pero nosotros sabemos que no es posible que haya otra opción. 

Tener que agregar siempre el else no es conveniente. Además, si agrega una nueva subclase, el compilador no detectará que algo ha cambiado y va a ir por el else. 

Kotlin proporciona una solución a este problema: Sealed classes  o clases selladas. Marcas una superclase con el modificador sellado, y eso restringe la posibilidad de crear subclases. Todas las subclases directas deben anidarse en la superclase:


sealed class Expr {

    class Num(val value: Int) : Expr()

    class Sum(val left: Expr, val right: Expr) : Expr()

}


fun eval(e: Expr): Int =

    when (e) {

        is Expr.Num -> e.value

        is Expr.Sum -> eval(e.right) + eval(e.left)

    }


Y listo!


El modificador sealed implica que la clase es abierta por lo tanto, no necesitamos agregar modificador open de forma explícita.

Otra cosa importante es que no puede declarar una interfaz sellada. ¿Por qué? Si pudiera, el compilador de Kotlin no podría garantizar que alguien no pueda implementar esta interfaz en el código Java.


lunes, 19 de septiembre de 2022

Clases internas y anidadas en Kotlin


Al igual que en Java, en Kotlin puedes declarar una clase en otra clase. Hacerlo puede ser útil para encapsular una clase auxiliar o colocar el código más cerca de donde se usa. La diferencia es que las clases anidadas de Kotlin no tienen acceso a la instancia de la clase externa, a menos que lo solicite específicamente. Veamos un ejemplo que muestra por qué esto es importante.

Imagine que desea definir un elemento View, cuyo estado se puede serializar. Puede que no sea fácil serializar una vista, pero puede copiar todos los datos necesarios en otra clase auxiliar. Declaras la interfaz State que implementa Serializable. La interfaz View declara los métodos getCurrentState y restoreState que se pueden usar para guardar el estado de una vista.

interface State: Serializable

interface View {

    fun getCurrentState(): State

    fun restoreState(state: State) {}

}

Es útil definir una clase que guarde el estado de un botón en la clase Botón. Veamos cómo se puede hacer en Java (el código Kotlin similar se mostrará en un momento).

/* Java */

public class Button implements View {

    @Override

    public State getCurrentState() {

        return new ButtonState();

    }

    @Override

    public void restoreState(State state) { /*...*/ }

    public class ButtonState implements State { /*...*/ }

}

La clase ButtonState que implementa la interfaz State y contiene información específica para Button. En el método getCurrentState, crea una nueva instancia de esta clase. En un caso real, inicializaría ButtonState con todos los datos necesarios.

¿Qué tiene de malo este código? ¿Por qué se obtiene un java.io.NotSerializableException? Eso puede parecer extraño al principio: ButtonState guarda una referencia a Button que no es Serializable.

Todo queda claro cuando recuerdas que en Java, cuando declaras una clase en otra clase, se convierte en una clase interna por defecto. La clase ButtonState del ejemplo almacena implícitamente una referencia a su clase Button externa. Eso explica por qué ButtonState no se puede serializar: Button no se puede serializar y la referencia a él interrumpe la serialización de ButtonState.

Para solucionar este problema, debe declarar la clase ButtonState como estática. Declarar una clase anidada como estática elimina la referencia implícita de esa clase a su clase adjunta. En Kotlin, el comportamiento predeterminado de las clases internas es lo opuesto a lo que acabamos de describir.

class Button : View {

override fun getCurrentState(): State = ButtonState()

override fun restoreState(state: State) { /*...*/ }

class ButtonState : State { /*...*/ }

}

Una clase anidada en Kotlin sin modificadores explícitos es lo mismo que una clase anidada estática en Java. Para convertirlo en una clase interna para que contenga una referencia a una clase externa se puede usar el modificador inner. 

La sintaxis para hacer referencia a una instancia de una clase externa en Kotlin también difiere de Java. Se escribe this@Outer para acceder a la clase Outer desde la clase Inner:

class Outer {

    inner class Inner {

        fun getOuterReference(): Outer = this@Outer

    }

}



miércoles, 7 de septiembre de 2022

Los modificadores de visibilidad de Kotlin y Java



Los modificadores public, protected, y private en Kotlin se conservan al compilar en el código de bytes de Java. Utiliza tales declaraciones de Kotlin del código Java como si se declararan con la misma visibilidad en Java. La única excepción es una private class: está compilada en una declaración de paquete privado bajo el capó (no puede hacer que una clase sea private en Java).

Pero, puede preguntarse, ¿qué sucede con el modificador internal? No hay un análogo directo en Java. La visibilidad privada del paquete es algo totalmente diferente: un módulo generalmente consta de varios paquetes, y diferentes módulos pueden contener declaraciones del mismo paquete. Por lo tanto, un modificador internal se vuelve public en el código de bytes. 

Esta correspondencia entre las declaraciones de Kotlin y sus análogos de Java (o su representación de código de bytes) explica por qué a veces puedes acceder a algo desde el código de Java a lo que no puedes acceder desde Kotlin. Por ejemplo, puede acceder a una clase internal o una declaración de nivel superior del código Java en otro módulo, o un miembro protected del código Java en el mismo paquete (similar a cómo lo hace en Java).

Pero tenga en cuenta que los nombres de los miembros internal de una clase están alterados. Técnicamente, los miembros internos se pueden usar desde Java, pero se ven feos en el código de Java. Eso ayuda a evitar conflictos inesperados en las anulaciones cuando extiende una clase de otro módulo y evita que use accidentalmente clases internal.

martes, 6 de septiembre de 2022

Modificadores de visibilidad en Kotlin


Al restringir la visibilidad de los detalles de implementación de una clase, nos aseguramos de poder cambiarlos sin el riesgo de romper el código que depende de la clase. Básicamente, los modificadores de visibilidad en Kotlin son similares a los de Java. Tiene los mismos modificadores public, protected y private. Pero la visibilidad predeterminada es diferente: si omite un modificador, la declaración se vuelve pública.

La visibilidad predeterminada en Java, paquete privado, no está presente en Kotlin. Kotlin usa paquetes solo como una forma de organizar el código en espacios de nombres; no los usa para el control de visibilidad. Como alternativa, Kotlin ofrece un nuevo modificador de visibilidad, internal, que significa “visible dentro de un módulo”. Un módulo es un conjunto de archivos Kotlin compilados juntos. Puede ser un módulo IntelliJ IDEA, un proyecto Eclipse, un proyecto Maven o Gradle, o un conjunto de archivos compilados con una invocación de una tarea Ant.

La ventaja de la visibilidad interna es que proporciona una encapsulación real para los detalles de implementación de su módulo. Con Java, la encapsulación se puede romper fácilmente, porque el código externo puede definir clases en los mismos paquetes utilizados por su código y, por lo tanto, obtener acceso a sus declaraciones privadas de paquetes.

Otra diferencia es que Kotlin permite el uso de visibilidad privada para declaraciones de nivel superior, incluidas clases, funciones y propiedades. Dichas declaraciones son visibles solo en el archivo donde se declaran. Esta es otra forma útil de ocultar los detalles de implementación de un subsistema. 

  • public: Por defecto y se puede ver en todos lados
  • internal: Visible en el modulo
  • protected: Visible en las subclases
  • private: Visible solo en la clase y en el archivo. 

Veamos un ejemplo. Cada línea de la función giveSpeech intenta violar las reglas de visibilidad. Se compila con un error.

internal open class TalkativeButton : Focusable {

    private fun yell() = println("Hey!")

    protected fun whisper() = println("Let's talk!")

}

fun TalkativeButton.giveSpeech() {

    yell()

     whisper()

}

Kotlin te prohíbe hacer referencia al tipo menos visible TalkativeButton (interno, en este caso) desde la función pública giveSpeech. Este es un caso de una regla general que requiere que todos los tipos usados ​​en la lista de tipos base y parámetros de tipo de una clase, o la firma de un método, sean tan visibles como la clase o el método mismo. Esta regla garantiza que siempre tenga acceso a todos los tipos que pueda necesitar para invocar la función o ampliar una clase. Para resolver el problema, puede hacer que la función sea interna o que la clase sea pública.

Tenga en cuenta la diferencia de comportamiento del modificador protected en Java y en Kotlin. En Java, puede acceder a un miembro protegido desde el mismo paquete, pero Kotlin no lo permite. En Kotlin, las reglas de visibilidad son simples y un miembro protegido solo es visible en la clase y sus subclases. También tenga en cuenta que las funciones de extensión de una clase no obtienen acceso a sus miembros privados o protegidos.

Una diferencia más en las reglas de visibilidad entre Kotlin y Java es que una clase externa no ve miembros privados de sus clases internas (o anidadas) en Kotlin.


martes, 23 de agosto de 2022

Modificadores en Kotlin


En Kotlin, como en Java, puede declarar una clase abstracta y no se pueden crear instancias de dichas clases. Una clase abstracta generalmente contiene miembros abstractos que no tienen implementaciones y deben anularse en las subclases. Los miembros abstractos siempre están abiertos, por lo que no necesita usar un modificador de apertura explícito:

abstract class Animated {

    abstract fun animate()

    open fun stopAnimating() { }

    fun animateTwice() { }

}

Veamos los diferentes tipos de modificadores: 

  • final: esta por defecto en las clases y no se puede sobreescribir el método
  • open: no esta por defecto las clases salvo que sea un método abstracto o interfaz y se puede sobreescribir
  • abstract: se debe sobreescribir y puede ser usado solo en clases abstractas
  • override: indica que estamos sobreescribiendo un método y es open por defecto salvo que le pongamos final. 

En las interfaces, no se usa final, open o abstract. Un miembro en una interfaz siempre está abierto; no puedes declararlo como definitivo o final. Es abstracto si no tiene cuerpo, pero no se requiere la palabra clave.




lunes, 22 de agosto de 2022

Sobre escribir métodos en Kotlin


Java permite crear subclases de cualquier clase y anular cualquier método, a menos que se haya marcado explícitamente con la palabra clave final. Esto a menudo es conveniente, pero también es problemático.

El llamado problema de la clase base frágil ocurre cuando las modificaciones de una clase base pueden causar un comportamiento incorrecto de las subclases porque el código modificado de la clase base ya no coincide con los supuestos en sus subclases. Si la clase no proporciona reglas exactas sobre cómo debe subclasificarse (qué métodos se supone que deben anularse y cómo), los clientes corren el riesgo de anular los métodos de una manera que el autor de la clase base no esperaba. Debido a que es imposible analizar todas las subclases, la clase base es "frágil" en el sentido de que cualquier cambio en ella puede provocar cambios inesperados en el comportamiento de las subclases.

Kotlin sigue la filosofía "diseñe y documente para herencia o, de lo contrario, prohíbalo". Mientras que las clases y los métodos de Java están abiertos de forma predeterminada, los de Kotlin son definitivos de forma predeterminada. Si desea permitir la creación de subclases de una clase, debe marcar la clase con el modificador open. Además, debe agregar el modificador open a cada propiedad o método que se pueda anular.


open class RichButton : Clickable {

    fun disable() {}

    open fun animate() {}

    override fun click() {}

}


Si anula un miembro de una clase o interfaz base, el miembro anulado también se abrirá de forma predeterminada. Si desea cambiar esto y prohibir que las subclases de su clase anulen su implementación, puede marcar explícitamente el miembro anulado como final:


open class RichButton : Clickable {

    final override fun click() {}

}



sábado, 13 de agosto de 2022

Interfaces en Kotlin


Las interfaces de Kotlin son similares a las de Java 8: pueden contener definiciones de métodos abstractos, así como implementaciones de métodos no abstractos (similares a los métodos predeterminados de Java 8), pero no pueden contener ningún estado. Para declarar una interfaz en Kotlin, se usa la palabra clave de interface en lugar de class.


interface Clickable {

    fun click()

}


Esto declara una interfaz con un solo método abstracto llamado clic. Todas las clases no abstractas que implementan la interfaz deben proporcionar una implementación de este método. 

class Button : Clickable { override fun click() = println("I was clicked") } 

>>> Button().click() 

I was clicked


Kotlin usa los dos puntos después del nombre de la clase para reemplazar las palabras clave extends e implements que se usan en Java. Como en Java, una clase puede implementar tantas interfaces como quiera, pero solo puede extender una clase.

El modificador override, similar a la anotación @Override en Java, se utiliza para marcar métodos y propiedades que anulan los de la superclase o la interfaz. A diferencia de Java, el uso del modificador de anulación es obligatorio en Kotlin. Esto le evita anular accidentalmente un método si se agrega después de escribir su implementación; su código no se compilará a menos que marque explícitamente el método como anulado o le cambie el nombre.

Un método de interfaz puede tener una implementación predeterminada. A diferencia de Java 8, que requiere que marques tales implementaciones con la palabra clave predeterminada, Kotlin no tiene una anotación especial para tales métodos: solo proporcionas un cuerpo de método. Cambiemos la interfaz Clickable agregando un método con una implementación predeterminada


interface Clickable { 

  fun click() 

  fun showOff() = println("I'm clickable!") 

}


Si implementa esta interfaz, debe proporcionar una implementación para hacer clic. Puede redefinir el comportamiento del método showOff o puede omitirlo si está de acuerdo con el comportamiento predeterminado. Supongamos ahora que otra interfaz también define un método showOff y tiene la siguiente implementación para él.


interface Focusable {

    fun setFocus(b: Boolean) = println("I ${if (b) "got" else "lost"} focus.")

    fun showOff() = println("I'm focusable!")

}


¿Qué sucede si necesita implementar ambas interfaces en su clase? Cada uno de ellos contiene un método showOff con una implementación predeterminada; ¿Qué implementación gana? Ninguno de los dos gana. En su lugar, obtiene el siguiente error del compilador si no implementa showOff explícitamente


The class 'Button' must override public open fun showOff() because it inherits many implementations of it


El compilador de Kotlin te obliga a proporcionar tu propia implementación.


class Button : Clickable, Focusable { 

    override fun click() = println("I was clicked")

    override fun showOff() { 

        super<Clickable>.showOff()

        super<Focusable>.showOff() } 

}


La clase Button ahora implementa dos interfaces. Implementa showOff() llamando a ambas implementaciones que heredó de los supertipos. Para invocar una implementación heredada, usa la misma palabra clave que en Java: super. Pero la sintaxis para seleccionar una implementación específica es diferente. Mientras que en Java puede colocar el nombre del tipo base antes de la palabra clave super, como en Clickable.super.showOff(), en Kotlin coloca el nombre del tipo base entre paréntesis angulares: super<Clickable>.showOff().

Si solo necesita invocar una implementación heredada, puede escribir esto:


override fun showOff() = super.showOff()


Puede crear una instancia de esta clase y verificar que se pueden llamar todos los métodos heredados


fun main(args: Array) { 

    val button = Button() 

    button.showOff() 

    button.setFocus(true) 

    button.click() 

}


La implementación de setFocus se declara en la interfaz Focusable y se hereda automáticamente en la clase Button.   

lunes, 1 de agosto de 2022

Log con Aop de Spring boot


Supongamos que queremos logear cada cosa que pasa por cada método de nuestros objetos, para hacer esto podemos utilizar aop. 

Veamos un ejemplo: 

@Aspect

@Configuration

@Order(0)

class LogingAspect {

    private val logger = LogFactory.getLog(LoggingAspect::class.java)


    @Pointcut("execution(public * com.paquete..*(..))")

    fun allControllersMethods() {

    }


    @Around("allControllersMethods()")

    fun profileAllMethods(pjp: ProceedingJoinPoint): Any {

        val method: Method = (pjp.signature as MethodSignature).method


        logger.info("Started ${method.name}")


        try {

            return if (pjp.args != null) {

                pjp.proceed(pjp.args)

            } else {

                pjp.proceed()

            }

        } catch (ex: Exception) {

            logger.error("Error in ${method.name}", ex)

            throw ex

        } finally {

            logger.info( "Finished ${method.name}")

        }

    }

}


Y Listo!!

jueves, 28 de julio de 2022

Compilar en kotlin


El código fuente de Kotlin normalmente se almacena en archivos con la extensión .kt. El compilador de Kotlin analiza el código fuente y genera archivos .class, al igual que lo hace el compilador de Java. Los archivos .class generados luego se empaquetan y ejecutan utilizando el procedimiento estándar para el tipo de aplicación en la que está trabajando. En el caso más simple, puede usar el comando kotlinc para compilar su código desde la línea de comandos y usar el comando java para ejecutar su código:


kotlinc <source file or directory> -include-runtime -d <jar name>

java -jar <jar name>


El código compilado con el compilador de Kotlin depende de la biblioteca de runtime de Kotlin. Contiene las definiciones de las propias clases de biblioteca estándar de Kotlin y las extensiones que Kotlin agrega a las API estándar de Java. La biblioteca de runtime debe distribuirse con su aplicación.

En la mayoría de los casos de la vida real, usará un sistema de compilación como Maven, Gradle o Ant para compilar su código. Kotlin es compatible con todos esos sistemas de compilación. Todos esos sistemas de compilación también admiten proyectos de lenguaje mixto que combinan Kotlin y Java en la misma base de código. Además, Maven y Gradle se encargan de incluir la biblioteca en tiempo de ejecución de Kotlin como una dependencia de tu aplicación.

miércoles, 27 de julio de 2022

Filosofía de Kotlin


Cuando hablamos de Kotlin, es un lenguaje pragmático, conciso y seguro con un enfoque en la interoperabilidad. ¿Qué significan exactamente con cada una de esas palabras? 

Pragmático: Kotlin es un lenguaje práctico diseñado para resolver problemas del mundo real. Su diseño se basa en muchos años de experiencia en la industria creando sistemas a gran escala, y sus funciones se eligen para abordar los casos de uso que encuentran muchos desarrolladores de software. Además, los desarrolladores tanto dentro de JetBrains como en la comunidad han estado usando las primeras versiones de Kotlin durante varios años y sus comentarios han dado forma a la versión lanzada del lenguaje. Kotlin puede ayudar a resolver problemas en proyectos reales.

Kotlin tampoco es un lenguaje de investigación. No estamos tratando de avanzar en el estado del arte en el diseño de lenguajes de programación y explorar ideas innovadoras en informática. En su lugar, siempre que sea posible, confiamos en funciones y soluciones que ya han aparecido en otros lenguajes de programación y han demostrado ser exitosas. Esto reduce la complejidad del lenguaje y hace que sea más fácil de aprender al permitirle confiar en conceptos familiares.

 Además, Kotlin no impone el uso de ningún estilo o paradigma de programación en particular. A medida que comienza a estudiar el lenguaje, puede usar el estilo y las técnicas que le resultan familiares de su experiencia con Java. Más adelante, descubrirá gradualmente las funciones más potentes de Kotlin y aprenderá a aplicarlas en su propio código, para hacerlo más conciso e idiomático.

Otro aspecto del pragmatismo de Kotlin es su enfoque en las herramientas. Un entorno de desarrollo inteligente es tan esencial para la productividad de un desarrollador como un buen lenguaje; y debido a eso, tratar el soporte IDE como una ocurrencia tardía no es una opción. En el caso de Kotlin, el complemento IntelliJ IDEA se desarrolló al unísono con el compilador, y las características del lenguaje siempre se diseñaron teniendo en cuenta las herramientas.

La compatibilidad con IDE también juega un papel importante a la hora de ayudarte a descubrir las características de Kotlin. En muchos casos, las herramientas detectarán automáticamente patrones de código comunes que se pueden reemplazar por construcciones más concisas y se ofrecerán a corregir el código por usted. Al estudiar las características del lenguaje utilizadas por las correcciones automáticas, también puede aprender a aplicar esas características en su propio código.

Conciso: Es bien sabido que los desarrolladores dedican más tiempo a leer código existente que a escribir código nuevo. Imagina que eres parte de un equipo que está desarrollando un gran proyecto y necesitas agregar una nueva función o corregir un error. ¿Cuáles son tus primeros pasos? Busca la sección exacta del código que necesita cambiar y solo entonces implementa una corrección. Lees mucho código para saber lo que tienes que hacer. Este código puede haber sido escrito recientemente por sus colegas, por alguien que ya no trabaja en el proyecto, o por usted, pero hace mucho tiempo. Solo después de comprender el código que lo rodea, puede realizar las modificaciones necesarias.

Cuanto más simple y conciso sea el código, más rápido entenderá lo que está pasando. Por supuesto, el buen diseño y los nombres expresivos juegan un papel importante aquí. Pero la elección del lenguaje y su concisión también son importantes. El lenguaje es conciso si su sintaxis expresa claramente la intención del código que lee y no lo oscurece con el texto estándar requerido para especificar cómo se logra la intención.

 En Kotlin, todo el código que escribe tiene un significado y no esta ahí solo para satisfacer los requisitos de la estructura del código. Gran parte del código estándar de Java, como getters, setters y la lógica para asignar parámetros de constructor a los campos, está implícito en Kotlin y no abarrota el código fuente.

Otra razón por la que el código puede ser innecesariamente largo es tener que escribir código explícito para realizar tareas comunes, como ubicar un elemento en una colección. Al igual que muchos otros lenguajes modernos, Kotlin tiene una rica biblioteca estándar que le permite reemplazar estas secciones largas y repetitivas de código con llamadas a métodos de biblioteca. La compatibilidad de Kotlin con lambdas facilita el paso de pequeños bloques de código a funciones de biblioteca. Esto le permite encapsular todas las partes comunes en la biblioteca y mantener solo la porción única y específica de la tarea en el código de usuario.

Al mismo tiempo, Kotlin no intenta reducir el código fuente al menor número de caracteres posible. Por ejemplo, aunque Kotlin admite la sobrecarga de operadores, los usuarios no pueden definir sus propios operadores. Por lo tanto, los desarrolladores de bibliotecas no pueden reemplazar los nombres de los métodos con secuencias de puntuación crípticas. Las palabras suelen ser más fáciles de leer que la puntuación y es más fácil encontrar documentación.

Un código más conciso requiere menos tiempo para escribir y, lo que es más importante, menos tiempo para leer. Esto mejora la productividad y permite hacer las cosas más rápido.

Seguro: En general, cuando hablamos de un lenguaje de programación como seguro, nos referimos a que su diseño previene ciertos tipos de errores en un programa. Por supuesto, esto no es un absoluto igualdad; ningún lenguaje previene todos los posibles errores. Además, la prevención de errores generalmente tiene un costo. Debe proporcionar al compilador más información sobre la operación prevista del programa, de modo que el compilador pueda verificar que la información coincida con lo que hace el programa. Por eso, siempre hay una compensación entre el nivel de seguridad que se obtiene y la pérdida de productividad necesaria para realizar anotaciones más detalladas.

Con Kotlin, se logra un mayor nivel de seguridad que en Java, con un costo total menor. La ejecución en la JVM ya proporciona muchas garantías de seguridad: por ejemplo, seguridad de la memoria, prevención de desbordamientos de búfer y otros problemas causados ​​por el uso incorrecto de la memoria asignada dinámicamente. Como lenguaje tipificado estáticamente en JVM, Kotlin también garantiza la seguridad de tipos de sus aplicaciones. Esto tiene un costo menor que con Java: no tiene que especificar todas las declaraciones de tipos, porque en muchos casos el compilador infiere los tipos automáticamente.

Kotlin también va más allá, lo que significa que se pueden evitar más errores mediante comprobaciones en tiempo de compilación en lugar de fallar en tiempo de ejecución. Lo más importante es que Kotlin se esfuerza por eliminar NullPointerException de su programa. El sistema de tipos de Kotlin realiza un seguimiento de los valores que pueden y no pueden ser nulos y prohíbe las operaciones que pueden conducir a una excepción NullPointer en tiempo de ejecución. El costo adicional requerido para esto es mínimo: marcar un tipo como anulable requiere solo un carácter, un signo de interrogación al final:

val s: String? = null

val s2: String = ""

Además, Kotlin proporciona muchas formas convenientes de manejar datos anulables. Esto es de gran ayuda para eliminar los bloqueos de aplicaciones.

Otro tipo de excepción que Kotlin ayuda a evitar es ClassCastException. Ocurre cuando conviertes un objeto en un tipo sin verificar primero que tenga el tipo correcto. En Java, los desarrolladores a menudo omiten la verificación, porque el nombre del tipo debe repetirse en la verificación y en el siguiente lanzamiento. En Kotlin, por otro lado, la verificación y el elenco se combinan en una sola operación: una vez que hayas verificado el tipo, puedes referirte a los miembros de ese tipo sin ningún casteo adicional. Por lo tanto, no hay motivo para omitir la verificación y no hay posibilidad de cometer un error. Así es como funciona esto:

if (value is String) println(value.toUpperCase())

Interoperable: Con respecto a la interoperabilidad, su primera preocupación probablemente sea: "¿Puedo usar mis bibliotecas existentes?" Con Kotlin, la respuesta es: "Sí, absolutamente". Independientemente del tipo de API que la biblioteca requiera que uses, puedes trabajar con ellas desde Kotlin. Puede llamar a métodos de Java, extender clases de Java e implementar interfaces, aplicar anotaciones de Java a sus clases de Kotlin, etc.

 A diferencia de otros lenguajes JVM, Kotlin va aún más allá con la interoperabilidad, lo que hace que llamar al código Kotlin desde Java también sea sencillo. No se requieren trucos: las clases y los métodos de Kotlin se pueden llamar exactamente como las clases y los métodos de Java normales. Esto le brinda la máxima flexibilidad para combinar código Java y Kotlin en cualquier parte de su proyecto. Cuando comience a adoptar Kotlin en su proyecto de Java, puede ejecutar el convertidor de Java a Kotlin en cualquier clase de su base de código, y el resto del código seguirá compilando y funcionando sin modificaciones. Esto funciona independientemente del rol de la clase que haya convertido.

Otra área en la que Kotlin se centra en la interoperabilidad es el uso de las bibliotecas Java existentes en la mayor medida posible. Por ejemplo, Kotlin no tiene su propia biblioteca de colecciones. Se basa completamente en las clases de la biblioteca estándar de Java y las amplía con funciones adicionales para un uso más conveniente en Kotlin. (Veremos el mecanismo para esto con más detalle en la sección 3.3). Esto significa que nunca necesitará envolver o convertir objetos cuando llame a las API de Java desde Kotlin, o viceversa. Toda la riqueza de API proporcionada por Kotlin viene sin costo en tiempo de ejecución.

 Las herramientas de Kotlin también brindan soporte completo para proyectos en varios lenguajes. Puede compilar una combinación arbitraria de archivos fuente de Java y Kotlin, independientemente de cómo dependan entre sí. Las funciones del IDE también funcionan en todos los lenguajes, lo que le permite:

  • Navegar libremente entre los archivos fuente de Java y Kotlin
  • Depurar proyectos de lenguaje mixto y pasar entre código escrito en diferentes lenguajes
  • Refactorizar sus métodos Java y actualice correctamente su uso en el código Kotlin, y viceversa. 



lunes, 25 de julio de 2022

Interceptor de Retrofit

                                                                                                                                                                                                                                                                        

Supongamos que tenemos que enviar en todas las llamadas a web services, un valor o un token o cualquier cosa. 

Lo que podemos hacer con retrofit es un interceptor, este lo declaramos una vez y para todas las llamadas enviarán los valores. Veamos un ejemplo: 

@Component

class RetrofitClient {


    @Value("\${service.url}")

    private lateinit var baseUrl: String


    private lateinit var retrofit: Retrofit


    private lateinit var objectMapper: ObjectMapper


    @PostConstruct

    fun init() {

        objectMapper = ObjectMapper()

            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

            .registerModule(KotlinModule())


        val client = OkHttpClient.Builder().addInterceptor(EjemploInterceptor()).build()


        retrofit = Retrofit.Builder()

            .baseUrl(baseUrl)

            .client(client)

            .addConverterFactory(JacksonConverterFactory.create(objectMapper))

            .build()

    }


    fun <T> buildService(service: Class<T>): T {

        return retrofit.create(service)

    }


}


class EjemploInterceptor : Interceptor {


    companion object {

        private const val EJEMPLO = "ejemplo"

    }


    override fun intercept(chain: Interceptor.Chain): Response = chain.run {

        proceed(

            request()

                .newBuilder()

                .addHeader(EJEMPLO, "valor a agregar")

                .build()

        )

    }

}


En este ejemplo en todas las llamadas enviarán el valor agregado en el head. Y listo! 

lunes, 18 de julio de 2022

Cualidades clave de Kotlin


Estáticamente tipado
: al igual que Java, Kotlin es un lenguaje de programación con tipos estáticos. Esto significa que el tipo de cada expresión en un programa se conoce en el momento de la compilación, y el compilador puede validar que los métodos y campos a los que intenta acceder existen en los objetos que está utilizando. Esto contrasta con los lenguajes de programación tipificados dinámicamente, que están presentes en la JVM, entre otros, Groovy, Clojure y JRuby. Esos lenguajes le permiten definir variables y funciones que pueden almacenar o devolver datos de cualquier tipo y resolver las referencias de métodos y campos en tiempo de ejecución. Esto permite un código más corto y una mayor flexibilidad en la creación de estructuras de datos. Pero la desventaja es que los problemas como los nombres mal escritos no se pueden detectar durante la compilación y pueden provocar errores de tiempo de ejecución.

Por otro lado, a diferencia de Java, Kotlin no requiere que especifiques el tipo de cada variable explícitamente en tu código fuente. En muchos casos, el tipo de una variable se puede determinar automáticamente a partir del contexto, lo que le permite omitir la declaración de tipo. Aquí está el ejemplo más simple posible de esto:

val x = 1

Estás declarando una variable y, como se inicializa con un valor entero, Kotlin determina automáticamente que su tipo es Int. La capacidad del compilador para determinar tipos a partir del contexto se denomina inferencia de tipos.

Los siguientes son algunos de los beneficios del tipado estático:

  • Rendimiento: llamar a los métodos es más rápido porque no es necesario averiguar en tiempo de ejecución qué método debe llamarse.
  • Confiabilidad: el compilador verifica la corrección del programa, por lo que hay menos posibilidades de fallas durante el tiempo de ejecución.
  • Capacidad de mantenimiento: trabajar con código desconocido es más fácil porque puede ver con qué tipo de objetos está trabajando el código.
  • Compatibilidad con herramientas: la escritura estática permite refactorizaciones confiables, finalización de código precisa y otras características de IDE.

Gracias a la compatibilidad de Kotlin con la inferencia de tipos, la mayor parte de la verbosidad adicional asociada con la escritura estática desaparece porque no es necesario declarar los tipos explícitamente.

Si observa los detalles del sistema de tipos de Kotlin, encontrará muchos conceptos familiares. Las clases, las interfaces y los genéricos funcionan de manera muy similar a Java, por lo que la mayor parte de su conocimiento de Java debería transferirse fácilmente a Kotlin. Sin embargo, algunas cosas son nuevas.

El más importante de ellos es el soporte de Kotlin para tipos anulables, lo que le permite escribir programas más confiables al detectar posibles excepciones de puntero nulo en el momento de la compilación. 

Otra cosa nueva en el sistema de tipos de Kotlin es su soporte para tipos de funciones. Para ver de qué se trata, veamos las ideas principales de la programación funcional y veamos cómo se admite en Kotlin.

Programación funcional y orientada a objetos: Como desarrollador de Java, sin duda estamos familiarizado con los conceptos básicos de la programación orientada a objetos, pero la programación funcional puede ser nueva para desarrolladores Java. Los conceptos clave de la programación funcional son los siguientes:

  • Funciones de primera clase: Puede almacenar funciones en variables, pasarlos como parámetros o devolverlos desde otras funciones.
  • Inmutabilidad: trabaja con objetos inmutables, lo que garantiza que su estado no puede cambiar después de su creación.
  • Sin efectos secundarios: utiliza funciones puras que devuelven el mismo resultado dadas las mismas entradas y no modifican el estado de otros objetos ni interactúan con el mundo exterior.

¿Qué beneficios puede obtener al escribir código en el estilo funcional? Primero, concisión. El código funcional puede ser más elegante y sucinto en comparación con su contraparte imperativa, porque trabajar con funciones como valores le brinda mucho más poder de abstracción, lo que le permite evitar la duplicación en su código.

Imagine que tiene dos fragmentos de código similares que implementan una tarea similar (por ejemplo, buscar un elemento coincidente en una colección) pero difieren en los detalles (cómo se detecta el elemento coincidente). Puede extraer fácilmente la parte común de la lógica en una función y pasar las partes diferentes como argumentos. Esos argumentos son en sí mismos funciones, pero puede expresarlos usando una sintaxis concisa para funciones anónimas llamadas expresiones lambda:

fun findAlice() = findPerson { it.name == "Alice" }

fun findBob() = findPerson { it.name == "Bob" }

El segundo beneficio del código funcional es el subprocesamiento múltiple seguro. Una de las mayores fuentes de errores en los programas de subprocesos múltiples es la modificación de los mismos datos de varios subprocesos sin la sincronización adecuada. Si usa estructuras de datos inmutables y funciones puras, puede estar seguro de que tales modificaciones inseguras no ocurrirán y no necesita idear esquemas de sincronización complicados.

Finalmente, la programación funcional significa pruebas más fáciles. Las funciones sin efectos secundarios se pueden probar de forma aislada sin necesidad de una gran cantidad de código de configuración para construir todo el entorno del que dependen.

En términos generales, el estilo funcional se puede usar con cualquier lenguaje de programación, incluido Java, y muchas partes del mismo se recomiendan como un buen estilo de programación. Pero no todos los lenguajes brindan el soporte sintáctico y de biblioteca necesario para usarlo sin esfuerzo; por ejemplo, este soporte faltaba en su mayoría en las versiones de Java anteriores a Java 8. Kotlin tiene un amplio conjunto de características para admitir la programación funcional desde el principio. Estos incluyen lo siguiente:

  • Tipos de funciones, que permiten que las funciones reciban otras funciones como parámetros o devuelvan otras funciones
  • Expresiones lambda, que le permiten pasar bloques de código con un mínimo de repeticiones
  • Clases de datos, que proporcionan una sintaxis concisa para crear objetos de valor inmutable
  • Un amplio conjunto de API en la biblioteca estándar para trabajar con objetos y colecciones en el estilo funcional

Kotlin le permite programar en el estilo funcional pero no lo impone. Cuando lo necesite, puede trabajar con datos mutables y escribir funciones que tienen efectos secundarios sin tener que pasar por obstáculos adicionales. Y, por supuesto, trabajar con marcos que se basan en interfaces y jerarquías de clases es tan fácil como con Java. Al escribir código en Kotlin, puede combinar los enfoques funcional y orientado a objetos y utilizar las herramientas más apropiadas para el problema que está resolviendo.

Gratuito y open source: El lenguaje Kotlin, incluido el compilador, las bibliotecas y todas las herramientas relacionadas, es completamente de código abierto y de uso gratuito para cualquier propósito. Está disponible bajo la licencia Apache 2; el desarrollo ocurre abiertamente en GitHub (http://github.com/jetbrains/kotlin), y las contribuciones de la comunidad son bienvenidas. También puede elegir entre tres IDE de código abierto para desarrollar sus aplicaciones de Kotlin: IntelliJ IDEA Community Edition, Android Studio y Eclipse son totalmente compatibles. (Por supuesto, IntelliJ IDEA Ultimate también funciona).