Author Archives: Gustavo

Golang leer archivos

Leer archivos es una de las tareas más básicas de cualquier programa y en Golang es bastante simple. Veremos en este post como abrir un archivo, leer su contenido e imprimirlo.

Si deseas leer un archivo de texto y visualizar su contenido solo debes hacer uso del paquete io/ioutil en conjunto con bufio de Golang.

Leer todo un archivo en memoria con Golang

Partamos de un ejemplo en el que leemos un archivo de texto en memoria con Go y luego imprimimos todo su contenido.

package main

import (
      "fmt"
      "io/ioutil"
      "log"
)

func main() {
      txt, err := ioutil.ReadFile("/Users/gp/file_read.txt")
      if err != nil {
             log.Fatal(err)
      }

      str := string(txt) // convert to a 'string'
      fmt.Println(str) // print the content
}
$ go run readFile.go 
hello word!
hi golang!

Obviamente para un archivo de gran tamaño una lectura de este modo no es recomendable.

Podemos hacer uso de un buffer para leer archivos de modo tal de ir leyendo partes del contenido total del archivo.

Leer todo un archivo línea por línea en Go

Para leer un archivo linea por linea puedes hacer uso de “bufio” y pedirle un Scanner que irás iterando acorde a determinados delimitadores que puedes usar con Scanner . Usualmente el delimitador más común es el fin de línea a fin de leer el archivo linea por linea.

Debes abrir el archivo y luego crear un scanner de este modo:

package main

import (
      "bufio"
      "fmt"
      "log"
      "os"
)

func main() {
      file, err := os.Open("/Users/gp/file_read_lines.txt")
      if err != nil {
             log.Fatal(err)
      }

      scanner := bufio.NewScanner(file) // default line by line
      for scanner.Scan() {
             fmt.Println(scanner.Text()) 
      }

      if err := scanner.Err(); err != nil {
             log.Fatal(err)
      }
}
$ go run readFileScanner.go
hello word!
hi golang!
hello a
hello b
hello c
hello d
bye!

Por default., bufio.NewScanner(file) devuelve un scanner delimitado por fin de linea pero podriamos obtener otro delimitado por palabras agregando scanner.Split(bufio.ScanWords)

package main

import (
      "bufio"
      "fmt"
      "log"
      "os"
)

func main() {
      file, err := os.Open("/Users/gp/file_read_lines.txt")
      if err != nil {
             log.Fatal(err)
      }

      scanner := bufio.NewScanner(file) // default line by line
      scanner.Split(bufio.ScanWords) // change to words

      for scanner.Scan() {
             fmt.Println(scanner.Text())
      }

      if err := scanner.Err(); err != nil {
             log.Fatal(err)
      }
}

Observa como la salida es ahora palabra por palabra:

$ go run readFileScannerWords.go
hello
word!
hi
golang!
hello
a
hello
b
hello
c
hello
d
bye!

Puedes ver este código en github y gitlab

Compartir esto:

Golang curso inicial

¿Cual es el propósito de este curso inicial de Go en Español?

Este curso inicial de Go intenta darte una introducción muy rápida (rapidísima 🙂 ) sobre las principales características de Golang con ejemplos simples. Todo el código de ejemplo está disponible tanto en github como gitlab. Una vez obtenido un pantallazo sobre el lenguaje podrás decidir si te gusta o no y si quieres continuar profundizando.

Acerca de Go

Go es un lenguaje bastante nuevo creado por Google. Se trata de un lenguaje compilado y tipado cuyo objetivo principal es permitir el desarrollo de programas escalables de forma simple y rápida.

¿Porque deberías aprender Golang? Responde esta pregunte contandote porque he yo querido aprender Go. Principalmente la elección de Golang fue por la facilidad en el manejo de concurrencia, la compilación en código nativo lo que lo hace extremadamente rápido en su ejecución y lo simple y cómodo que es para el desarrollo.

Quien esta usando Golang en producción. Puedes encontrar una buena respuesta en este enlace de GoUsers actualizado.

Nota: este curso está en desarrollo, me faltan temas aún por pulir pero todo lo que leas aquí hasta el momento te servirá perfectamente  😉

Introducción

Variables, Types, Constants, Arrays

Functions and Packages

Condicionales y loop

Métodos e Interfaces

Concurrency

Desarrollo de una app en golang

  • Este punto está en construcción.
Compartir esto:

Golang Select

Select te permite esperar y elegir el resultado de operaciones que has realizado usando channels. Esta instrucción es bloqueante, por lo que bloquea el flujo de código hasta tanto una de las operaciones de envío y recepción haya finalizado. En el caso de que haya varias operaciones listas al mismo tiempo, ‘go’ elige uno al azar.

Veamos el siguiente ejemplo en el que creamos dos funciones. Una de ellas simula una operación que se ejecuta rápidamente y la otra que demora más tiempo.

package main

import (
      "time"
      "fmt"
)

func calculateFast(ch chan <- int, value int) {
      time.Sleep(3 * time.Second) // simulate 3 second of processing
      ch <- value * 10
}

func calculateSlow(ch chan <- int, value int) {
      time.Sleep(5 * time.Second) // simulate 5 second of processing
      ch <- value * 10
}

func main() {
      c1 := make(chan int)
      c2 := make(chan int)

      go calculateFast(c1, 10)
      go calculateSlow(c2, 20)

      select {
      case msg1 := <-c1:
             fmt.Println("received", msg1)  // 100
      case msg2 := <-c2:
             fmt.Println("received", msg2) // 200 
      }
}

En el ejemplo de arriba, usando goroutines y channels llamamos a ambas funciones y luego con select esperamos el resultado de alguna de ellas recibiendo para este ejemplo, el resultado de la función que termina antes su procesamiento.

run select.go
received 100

Agrega al ejemplo anterior un ciclo for para probar cómo recibes ambos resultados.

package main

import (
      "time"
      "fmt"
)

func calculateFast(ch chan <- int, value int) {
      time.Sleep(3 * time.Second) // simulate 3 second of processing
      ch <- value * 10
}

func calculateSlow(ch chan <- int, value int) {
      time.Sleep(5 * time.Second) // simulate 5 second of processing
      ch <- value * 10
}

func main() {
      c1 := make(chan int)
      c2 := make(chan int)

      go calculateFast(c1, 10)
      go calculateSlow(c2, 20)

      for i := 0; i < 2; i++ {
             select {
             case msg1 := <-c1:
                    fmt.Println("received", msg1)  // 100
             case msg2 := <-c2:
                    fmt.Println("received", msg2) // 200
             }
      }
}

Agregamos un ciclo de dos vueltas, porque sabemos de antemano que son dos llamadas a métodos. En la primer vuelta recibimos el valor de la ejecución más rápida y en la segunda el de la segunda función más lenta.

La salida de esta ejecución muestra el resultado de ambas rutinas y un tiempo de 5 segundos en vista de que ambos procesos (dos segundos y cinco segundos de procesamiento) se ejecutan concurrentemente.

$ time go run select.go 
received 100
received 200

real    0m5.317s

Usar default case en un select

El “default case” en un select se ejecuta cuando ninguno de los casos anteriores está listo para ejecutarse.

Realiza otro cambio en el código que estamos usando de ejemplo, ampliando el for a un valor de 5 ciclos, esperando un segundo en cada ciclo y agregando un default case.

package main

import (
      "time"
      "fmt"
)

func calculateFast(ch chan <- int, value int) {
      time.Sleep(3 * time.Second) // simulate 3 second of processing
      ch <- value * 10
}

func calculateSlow(ch chan <- int, value int) {
      time.Sleep(5 * time.Second) // simulate 5 second of processing
      ch <- value * 10
}

func main() {
      c1 := make(chan int)
      c2 := make(chan int)

      go calculateFast(c1, 10)
      go calculateSlow(c2, 20)

      for i := 0; i < 5; i++ {
             time.Sleep(1 * time.Second)
             select {
             case msg1 := <-c1:
                    fmt.Println("received", msg1)  // 100
             case msg2 := <-c2:
                    fmt.Println("received", msg2) // 200
             default:
                    fmt.Println("nothing received yet")
             }
      }
}

Estudiemos el resultado:

Tenemos un ciclo de 5 vueltas. Entre cada vuelta esperamos 1 segundo. Total del ciclo 5 segundos.
La primer función demora 3 segundos.
La segunda función demora 5 segundos.

Entonces, en los primeros dos segundos no tenemos ningún resultado.

Recién en el tercer segundo recibimos el resultado de la primer función.

Por último, en el último ciclo recibimos el resultado de la otra función.

Golang select default case

Golang select default case

$ time go run select.go 
nothing received yet
nothing received yet
received 100
nothing received yet
received 200

real    0m5.325s

Conclusión:

Con select puedes esperar por múltiples operaciones que has ejecutado recibiendo el resultado en cada case.

Puedes ver este código en github y gitlab

Compartir esto:

Golang Closures

Para comprender los closures en Go primero debemos comprender sobre funciones anónimas.

Las funciones anónimas son simplemente funciones que hemos creado sin un nombre y que luego asignamos a una variable o que usamos directamente.

Veamos el ejemplo a continuación en donde hemos asignado una función a la variable “a” . No hay nombre definido para la función y podemos usarla a través de la variable a la cual la función está asignada.

package main

import "fmt"

func main() {
      a := func() {
             fmt.Println("hello golang!")
      }
      a()  // use var a like a funcion
}

También podemos usar una función anónima directamente sin asignarla a una variable de esta manera:

package main

import "fmt"

func main() {
      func() {
             fmt.Println("hello golang!")
      }()
}

Golang Closures

Un Closure es una función anónima que accede a una variable definida fuera de dicha función.

La siguiente función accede a la variable “a”

package main

import "fmt"

func main() {
      a := 1
      func() {
             fmt.Println("value of a: ", a)
      }()
}

Cada Closures es dueño de sus variables. ¿Qué significa esto? Veamos un ejemplo para comprenderlo.

La función “sayHello” devuelve otra función que define y retorna una función anonima en el cuerpo de “sayHello”. La función retornada encierra (closure) la variable “h” .

Observa que asignamos un funciones Closure a dos variables, a y b.
La función asignada a la variable “a” maneja el valor de “h” sin confundirse con el valor en la función asignada a la variable “b”. Dicho de otro modo cada función a y b encierra su variable h.

package main

import "fmt"

func sayHello() func(string) string {
      h := ""
      return func(b string) string {
             h = h + " " +  b
             return h
      }
}

func main() {
      a := sayHello()
      b := sayHello()

      fmt.Println(a("Hello golang"))
      fmt.Println(a("how are you?"))

      fmt.Println(b("Hi!"))
      fmt.Println(b("what up?"))
}

El resultado de esto es:

go run closures.go
 Hello golang
 Hello golang how are you?
 Hi!
 Hi! what up?

Veamos otro ejemplo muy simple. A continuación tenemos una función closure que solo va sumando de dos en dos. Creamos dos variables a y b para dicha función y la ejecutamos.

package main

import "fmt"

func seqOfTwo() func() int {
      i := 0
      return func() int {
             i = i + 2
             return i
      }
}

func main() {
      a := seqOfTwo()
      fmt.Println(a())
      fmt.Println(a())
      fmt.Println(a())

      b := seqOfTwo()
      fmt.Println(b())
      fmt.Println(b())
      fmt.Println(b())
}

El resultado

go run closures2.go
2
4
6
2
4
6

Conclusión:

Has visto en este post que es un closure en golang.

Puedes ver este código en github y gitlab

Compartir esto:

Go Channels

Qué son los channels en Golang

En Golang los channels son canales que usas para comunicarte con Goroutines.
A través de estos canales puedes enviar de un extremo a otro información.
Para este fin utilizamos el operador <-

Go channel ejemplo

Cómo se crea un channel en Golang

Para crear un canal debes usar make y definir el tipo.
En este caso definimos un channel de tipo string

mychannel := make(chan string)

Cómo se envían mensajes a través de un channel

Para escribir un mensajes a través de un channel y para leer lo haces de este modo.

Escribo en el channel “mychannel” el contenido de “data”

mychannel <- data // write to channel 

Leo desde en channel “mychannel” su contenido y lo almaceno en “data”

data := <- mychannel // read from channel  

Observa que la dirección de la flecha “<-” respecto al chanel nos indica
si estamos escribiendo en en channel o si estamos leyendo desde el channel.

Como se usan los channels en Go

Vamos a crear una función muy simple que reciba un channel y un valor entero.
Luego escribiremos en en channel la multiplicación del valor entero por 10.

En nuestro main crearemos un channel usando make y llamaremos a la función enviando el channel que luego leeremos.

func calculate(ch chan <- int, value int) {
      ch <- value * 10 // write value 10 into channel
}

func main() {

      channel := make(chan int)  // create channel

      go calculate(channel, 5)
      go calculate(channel, 10)

      v1 := <-channel // read channel
      v2 := <-channel // read channel

      fmt.Println(v1)
      fmt.Println(v2)

      fmt.Scanln() // wait, press a key
      fmt.Println("done")
}

La salida de este ejemplo:

go run channels.go
100
50
done

Veamos otro ejemplo de Goroutines con channels

Escribiremos un pequeño programa que hará dos cálculos, suma y multiplicación recibiendo los parámetros para cada operación. También se recibe por parámetro el channel para escribir el resultado en él.
Luego leeremos el channel y lo guardaremos en una variable para imprimirlo.

package main

import (
      "fmt"
)

func calcSum(number1 int, number2 int, chsum chan int) {
      sum := number1 + number2
      chsum <- sum
}

func calcMultiplication(number1 int, number2 int, chprod chan int) {
      prod := number1 * number2
      chprod <- prod
}

func main() {
      sumCh := make(chan int)
      prodCh := make(chan int)
      go calcSum(20, 30, sumCh)
      go calcMultiplication(40, 50, prodCh)
      sum, prod := <-sumCh, <-prodCh
      fmt.Println("Result sum", sum)
      fmt.Println("Result prod", prod)
}

La salida de este ejemplo:

go run channels2.go
Result sum 50
Result prod 2000

Channel Deadlock

Si intentas escribir en un channel fuera de una go routine recibirás un error Deadlock.
En el siguiente código se crea un channel, y se envía a la función que luego escribirá en el canal el resultado. Sin embargo esta función no es una go routine porque no se antepuso “go” para llamarla.

package main

import "fmt"

func sum(n1 int, n2 int, chsum chan int) {
      sum := n1 + n2
      chsum <- sum
}

func main() {
      ch := make(chan int)
      //go sum(2, 5, ch)  // correct
      sum(2, 5, ch)  // incorrect
      result := <-ch
      fmt.Println(result)
}

La salida de este ejemplo:

$ go run channelsDeadLock.go
fatal error: all goroutines are asleep - deadlock!
..
Process finished with exit code 1

Channel Buffered

Por defecto podemos enviar un valor sobre un channel siempre que tengamos su correspondiente receptor. Sin embargo, con los Channels Buffered esto no es requerido.
Los Channels Buffered pueden aceptar un número determinado de valores sin el correspondiente receptor de esos valores.

Como se define un channel buffered en Golang

Aqui definimos un channel con una capacidad de tres.

channel := make(chan int, 3) 

Dijimos antes que podemos enviar los valores al channel y no es obligatorio leer el valor inmediatamente porque es un channel buffered.

Observa a continuación que creamos un channel con una capacidad de tres y que solo leemos uno.

package main

import "fmt"

func addOneHundred(message chan <- int, value int) {
      message <- value + 100
}

func main() {
      channel := make(chan int, 2)  // create channel

      go addOneHundred(channel, 100) // send value 100 to channel
      go addOneHundred(channel, 200) // send value 200 to channel
      go addOneHundred(channel, 300) // send value 200 to channel

      v1 := <-channel // read channel
      //v2 := <-channel // read channel - not mandatory
      //v3 := <-channel // read channel - not mandatory

      fmt.Println("Value", v1)
      //fmt.Println("Value", v2)
      //fmt.Println("Value", v3)
}

Puedes ver entonces que tenemos dos tipos de channel que se ilustran muy bien con estas dos imágenes:

Channel unbuffered

Channel Buffered



Imágenes de ardanlabs

Conclusión:

Has visto en este post como crear un channel y cómo utilizarlo para enviar y recibir información sobre él.

Puedes ver este código en github y gitlab

Compartir esto:

Como usar Spring Boot Retry

Descripción

Spring Boot Retry ofrece una forma simple para reintentar alguna operación que ha fallado. Esto es útil sobre todo cuando se tratan errores temporales o transitorios como el acceso a un recurso externo.
Spring Boot Retry puede configurarse de forma declarativa mediante anotaciones o definiendo una config general.

Vamos a ver aquí cómo utilizar la funcionalidad Retry dentro de SpringBoot.

Dependencias necesarias para Spring Boot Retry

Las dependencias que necesitas son las siguientes

<dependencies>

   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework.retry</groupId>
       <artifactId>spring-retry</artifactId>
   </dependency>

   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-aop</artifactId>
   </dependency>

   <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
   </dependency>

</dependencies>

Como activar Retry en Spring Boot

Para activar Spring Retry debes anotar tu aplicación @EnableRetry

@SpringBootApplication
@EnableRetry
public class Application {

   public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
   }

}

Como indicar que un método es ‘reintentable’

Para indicar que un método es reintentable usas esta anotación @Retryable de este modo. Puedes además establecer la máxima cantidad de intentos maxAttempts, y la demora entre intentos backoff entre otras opciones.

Para recuperarse de un método que, a pesar de los reintentos, sigue fallando puedes anotar un método de recuperación @Recover que será llamado por Spring al agotar todos los intentos. Este método debe devolver el mismo tipo de dato que el método original. Observa que puedes recibir como primer parámetro la excepción y subsecuentemente el resto de parametros del metodo que ha fallado.

@Service
public class RetryExampleService {

   private static Logger LOGGER = LoggerFactory.getLogger(RetryExampleService.class);

   @Retryable(value = {RuntimeException.class}, maxAttempts = 4, backoff = @Backoff(1000))
   public String retryExample(String s) {
       LOGGER.info(String.format("Retry retryTemplateExample %d", LocalDateTime.now().getSecond()));
       if (s == "error") {
           throw new RuntimeException("Error in RetryExampleService.retryExample ");
       } else {
           return "Hi " + s;
       }
   }

   @Recover
   public String retryExampleRecovery(RuntimeException t, String s) {
       LOGGER.info(String.format("Retry Recovery - %s", t.getMessage()));
       return "Retry Recovery OK!";
   }
}

Probar Retry con un test

Vamos a probar si nuestro método puede recuperarse con @Recovery.
Nuestro método de recuperación para este ejemplo simplemente devuelve un String “Retry Recovery OK!”

@RunWith(SpringRunner.class)
@SpringBootTest
public class RetryExampleServiceTest {

   @Autowired
   private RetryExampleService retryExampleService;

   @Test
   public void retryExampleWithRecoveryTest() throws Exception {
       String result = retryExampleService.retryExample("error");
       Assert.assertEquals("Retry Recovery OK!", result);
   }

}

La salida de este test RetryExampleServiceTest

INFO 74514 --- [main] com.gp.service.RetryExampleService       : Retry retryTemplateExample 52
INFO 74514 --- [main] com.gp.service.RetryExampleService       : Retry retryTemplateExample 53
INFO 74514 --- [main] com.gp.service.RetryExampleService       : Retry retryTemplateExample 54
INFO 74514 --- [main] com.gp.service.RetryExampleService       : Retry retryTemplateExample 55
INFO 74514 --- [main] com.gp.service.RetryExampleService       : Retry Recovery - Error in RetryExampleService.retryExample 

Con definir un template para Retry usando RetryTemplate

Además de usar las anotaciones @Retryable
y @Recover puedes crear un template en tu configuración para usarlo de manera general.

Para definir un template para los Retry en tu config defines un bean usando org.springframework.retry.support.RetryTemplate. Básicamente tienes dos atributos importantes: cantidad de intentos y el tiempo entre reintentos.

Si deseas puedes también definir un listener a partir de la clase RetryListenerSupport
y registrarlo en el template.

@Configuration
public class ApiConfig {

   @Bean
   public RetryTemplate retryTemplate() {

       RetryTemplate retryTemplate = new RetryTemplate();

       FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
       fixedBackOffPolicy.setBackOffPeriod(1000L);
       retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

       SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
       retryPolicy.setMaxAttempts(4);
       retryTemplate.setRetryPolicy(retryPolicy);

       retryTemplate.registerListener(new ApiRetryListener());

       return retryTemplate;
   }

}

Para el listener extiendes de RetryListenerSupport en donde tienes definido tres métodos, open cuando se inicia, onError al momento de producirse el error y reintentar y close cuando se agotaron todos los intentos.

public class ApiRetryListener extends RetryListenerSupport {

   private static Logger LOGGER = LoggerFactory.getLogger(ApiRetryListener.class);

   @Override
   public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
       LOGGER.info("ApiRetryListener.close");
       super.close(context, callback, throwable);
   }

   @Override
   public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
       LOGGER.info("ApiRetryListener.onError");
       super.onError(context, callback, throwable);
   }

   @Override
   public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
       LOGGER.info("ApiRetryListener.open");
       return super.open(context, callback);
   }

}

Luego para utilizar este template usas el bean que definiste llamando al método execute , org.springframework.retry.support.RetryTemplate#execute(org.springframework.retry.RetryCallback)

@Service
public class RetryTemplateExampleService {

   private static Logger LOGGER = LoggerFactory.getLogger(RetryTemplateExampleService.class);
   private final RetryTemplate retryTemplate;

   @Autowired
   public RetryTemplateExampleService(RetryTemplate retryTemplate) {
       this.retryTemplate = retryTemplate;
   }

   public String retryTemplateExample(String s) {
       String result;
       result = retryTemplate.execute(new RetryCallback<String, RuntimeException>() {
           @Override
           public String doWithRetry(RetryContext retryContext) {
               // do something in this service
               LOGGER.info(String.format("Retry retryTemplateExample %d", LocalDateTime.now().getSecond()));
               if (s == "error") {
                   throw new RuntimeException("Error in ExampleRetryTemplateService.retryTemplateExample ");
               } else {
                   return "Hi " + s;
               }
           }
       });
       LOGGER.info(String.format("Returning %s", result));
       return result;
   }

}

Probar RetryTemplate con un test

@RunWith(SpringRunner.class)
@SpringBootTest
public class RetryTemplateExampleServiceTest {

   @Autowired
   private RetryTemplateExampleService retryTemplateExampleService;

   @Test(expected = RuntimeException.class)
   public void retryTemplateExampleShouldThrowRuntime() throws Exception {
       retryTemplateExampleService.retryTemplateExample("error");
   }

   @Test
   public void retryTemplateExampleShouldReturnCorrectValue() throws Exception {
       String s = retryTemplateExampleService.retryTemplateExample("word");
       Assert.assertEquals("Hi word", s);
   }

}

La salida de este test

// retryTemplateExampleShouldThrowRuntime
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.open
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Retry retryTemplateExample 29
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.onError
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Retry retryTemplateExample 30
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.onError
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Retry retryTemplateExample 31
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.onError
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Retry retryTemplateExample 32
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.onError
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.close


// retryTemplateExampleShouldReturnCorrectValue
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.open
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Retry retryTemplateExample 32
INFO 75533 --- [main] com.gp.config.ApiRetryListener           : ApiRetryListener.close
INFO 75533 --- [main] c.g.service.RetryTemplateExampleService  : Returning Hi word

Conclusión

Hemos visto cómo usar Spring Retry para reintentar alguna operación en nuestro código si ha fallado y cómo crear un método de recuperación si lo necesitaramos.
Vimos que podemos definir esto simplemente con una anotación o con un template a usar.

Puedes ver este código completo

Compartir esto:

Java como cambiar el time zone a un Date

Vamos a examinar cómo cambiar el time zone de un date en java de distintos modos.

Como cambiar el timezone de un java.util.Date usando SimpleDateFormat

Debemos tener claro que la clase Date en Java no tiene time zone.
java.util.Date representa la cantidad de segundos transcurridos desde la medianoche 1 de enero de 1970 a las cero horas (hora UTC).

Esto significa que siempre el java.util.Date nos devolverá la hora para el time zone que este por default definida para el sistema.

Lo que necesitas hacer para cambiar el time zone a un Date es:
-Crear un DateFormat con el time zone para la zona que queremos
-Aplicar el DateFormat al Date obteniendo un string con modificado
-Volver a crear un object Date a partir de ese string

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class TimeZoneChangeDateExample {

    private static final String DATE_FORMAT = "dd-MM-yyyy hh:mm:ss a";

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

        // the date
        Calendar cal = Calendar.getInstance();
        cal.set(2017, 06, 29, 8, 30);
        Date date = cal.getTime();

        // format with tz
        TimeZone timeZone = TimeZone.getTimeZone("Europe/Amsterdam");
        SimpleDateFormat formatterWithTimeZone = new SimpleDateFormat(DATE_FORMAT);
        formatterWithTimeZone.setTimeZone(timeZone);

        // change tz using formatter
        String sDate = formatterWithTimeZone.format(date);

        // string to object date
        SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
        Date dateWithTimeZone = formatter.parse(sDate); // string to Date Object


        System.out.println("The actual date is: " + formatter.format(date));
        System.out.println("The date in " + timeZone.getID() + " is: " + sDate);
        System.out.println("Object date: " + formatter.format(dateWithTimeZone));
    }
}

La salida de este ejemplo:

The actual date is: 29-07-2017 08:30:54 AM
The date in Europe/Amsterdam is: 29-07-2017 01:30:54 PM
Object date: 29-07-2017 01:30:54 PM

Como cambiar el timezone de un Date usando Calendar

Para cambiar el time zone usando calendar debes:
-Crear un calendar
-Setear el time zone al calendar
-Obtener los valores a través de los gets especificos. *

* Nota: si intentas obtener el date directamente calendar.getTime()) obtendrás la fecha siempre pasada al la tiempo local porque lo que devuelve es un java.util.Date y como dijimos antes este no tiene el tz.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class TimeZoneChangeCalendarExample {

    private static final String DATE_FORMAT = "dd-MM-yyyy hh:mm:ss a";

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

        // calendar with tx
        TimeZone timeZone = TimeZone.getTimeZone("Europe/Amsterdam");
        Calendar calendar = Calendar.getInstance();
        calendar.set(2017, 06, 29, 8, 30);
        calendar.setTimeZone(timeZone);

        // get calendar using fields
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
        System.out.println("The actual date is: " + formatter.format(calendar.getTime()));
        System.out.print("The date in " + timeZone.getID() + " is: ");
        System.out.print(" Day:" + dayOfMonth);
        System.out.print(" Month:" + (month + 1)); // base zero
        System.out.print(" Year:" + year);
        System.out.print(" Hour:" + hourOfDay);
        System.out.print(" Min:" + minute);
        System.out.print(" Sec:" + second);

        System.out.println();

        // calendar getTime with DateFormat
        SimpleDateFormat formatterWithTimeZone = new SimpleDateFormat(DATE_FORMAT);
        formatterWithTimeZone.setTimeZone(timeZone);
        System.out.println("Calendar.getTime() - Date in Local TZ:" + formatterWithTimeZone.format(calendar.getTime()));

    }
}

La salida de este ejemplo:

The actual date is: 29-07-2017 03:30:22 AM
The date in Europe/Amsterdam is:  Day:29 Month:7 Year:2017 Hour:8 Min:30 Sec:22
Calendar.getTime() - Date in Local TZ:29-07-2017 08:30:22 AM

Como cambiar el timezone de un Date usando Java 8 en adelante

Para cambiar el time zone de una fecha en Java 8+:
-Crear un LocalDateTime
-Setear el time zone original al LocalDateTime
-Cambiar el time zone de la fecha original

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class TimeZoneChangeZonedDateTime {

    public static void main(String[] args) {

        LocalDateTime localDateTime = LocalDateTime.of(2018, 06, 29, 8, 30);

        // zone from Buenos Aires
        ZoneId zoneBuenosAires = ZoneId.of("America/Buenos_Aires");
        ZonedDateTime asiaZonedDateTime = localDateTime.atZone(zoneBuenosAires);

        // zone from Amsterdan
        ZoneId zoneAmsterdan = ZoneId.of("Europe/Amsterdam");
        ZonedDateTime nyDateTime = asiaZonedDateTime.withZoneSameInstant(zoneAmsterdan);

        // print dateTime with tz
        System.out.println("Date : " + asiaZonedDateTime);
        System.out.println("Date : " + nyDateTime);

    }
}

La salida de este ejemplo:

Date : 2018-06-29T08:30-03:00[America/Buenos_Aires]
Date : 2018-06-29T13:30+02:00[Europe/Amsterdam]

Puedes visitar este post con mayor detalle sobre la nueva api java.time

Conclusión

Este post muestra tres formas de cambiar el time zone para una fecha, partiendo sobre cómo cambiar el time zone para un java.util.Date, luego para un java.util.Calendar y por último en Java8+ haciendo uso de la nueva api java.time.

Compartir esto:

Java Convertir un List a un Map usando Stream

En este articulo veras como convertir un list a un map utilizando para ello un Stream disponible desde Java 8.

¿Cómo convertir un List a un Map?

Partiendo de una lista de productos creamos un Stream

public class Product {

   private long id;
   private String name;
   private String type;
   private Double price;

   public Product() {
   }

   public Product(long id, String name, String type, double price) {
       this.id = id;
       this.name = name;
       this.type = type;
       this.price = price;
   }

 //...

   @Override
   public String toString() {
       return "Product{" +
               "id=" + id +
               ", name='" + name + '\'' +
               ", type='" + type + '\'' +
               ", price=" + price +
               '}';
   }
}

List<Product> productList = new ArrayList<>();
productList.add(new Product(1, "SONY", "TV", 100));
productList.add(new Product(2, "SAMSUNG", "MOBILE", 200));

Stream<Product> productStream = productList.stream();

Usar Collectors.toMap para crear un map

Luego haces uso del Stream y “Collectors.toMap” en donde para este ejemplo usamos el id del producto como key del mapa y el nombre como valor.

// Stream to Map with Collectors.toMap
Map<Long, String> productMap = productStream.collect(Collectors.toMap(Product::getId, Product::getName));
productMap.forEach((k,v)->System.out.println("key : " + k + " value : " + v));

// output:
// key : 1 value : SONY
// key : 2 value : SAMSUNG
// key : 3 value : LG

Usar Function.identity() para guardar el objeto en el value del mapa

Si deseas usar como valor el mismo objeto puedes hacer uso de Function.identity() para el value

Map<Long, Product> productMapObject = productList.stream().collect(Collectors.toMap(Product::getId, Function.identity()));

// output
// key : 1 value : Product{id=1, name='SONY', type='TV', price=100.0}
// key : 2 value : Product{id=2, name='SAMSUNG', type='MOBILE', price=200.0}
// key : 3 value : Product{id=3, name='LG', type='MOBILE', price=300.0}

Evitando Duplicate key

Si en en la lista y por consiguiente en el Stream tienes dos elementos con un mismo id recibirás un IllegalStateException al tratar de crear el mapa.

// Exception 
productList.add(new Product(2, "SAMSUNG DUPLICATE", "MOBILE", 200));
try {
   Map<Long, String> productMapError = productList.stream().collect(Collectors.toMap(Product::getId, Product::getName));
} catch (IllegalStateException e) {
   e.printStackTrace();
}

El error de salida es confuso porque muestra el valor del mapa en vez de mostrar el key que se duplica.

Exception in thread "main" java.lang.IllegalStateException: Duplicate key SAMSUNG
//..

Para resolver esto pasas una función que realice el tratamiento de los duplicados para evitar este error:

// handle duplicate ids
Map<Long, String> list =
       productStream
               .collect(Collectors.toMap(
                       Product::getId,
                       Product::getName,
                       (oldValue, newValue) -> oldValue
               ));


// output:
// key : 1 value : SONY
// key : 2 value : SAMSUNG
// key : 3 value : LG

Básicamente lo que haces es decidir entre los dos valores duplicados con cual deseas quedarte. (oldValue, newValue) -> oldValue

Creando un mapa ordenado

Para crear un mapa ordenado solo debes indicarle al Stream el ordenamiento que deseas pasándole el comparador que deseas, en este ejemplo ordenamos con el key (id del producto).

// creating a ordered map
Map orderMap = productList.stream()
       .sorted(Comparator.comparingLong(Product::getId).reversed())
       .collect(
               Collectors.toMap(
                       Product::getId, Product::getName,
                       (oldValue, newValue) -> oldValue,  // if duplicate
                       LinkedHashMap::new  // use LinkedHashMap to keep order
               ));

orderMap.forEach((k, v) -> System.out.println("key : " + k + " value : " + v));

// output:
// key : 3 value : LG
// key : 2 value : SAMSUNG
// key : 1 value : SONY

Conclusión: usando Stream para colectar el resultado con Collectors.toMap creas un mapa a partir de tu lista. Luego puedes evitar duplicados pasándole una función para decidir qué hacer en caso de encontrarse con un key duplicado. .

Mira este código completo

Compartir esto:

Go como usar Goroutines

Un Goroutine es un hilo de ejecución ligero manejado por GO.

Gorutine son funciones o métodos que se ejecutan simultáneamente.
Podemos entenderlas como “hilos ligeros” con un costo mucho menor que un hilo tradicional.

Supongamos que tienes dos funciones

func numbers() {
	for i := 0; i < 5; i++ {
		fmt.Printf("%d ", i)
		time.Sleep(100 * time.Millisecond)
	}
}

func characters() {
	for i := 'a'; i <= 'f'; i++ {
		fmt.Printf("%c ", i)
		time.Sleep(200 * time.Millisecond)
	}
}

Si llamas a estas funciones de la forma habitual tendrías este resultado.

func main() {

	// without goroutine
	numbers()

	characters()

	fmt.Scanln() // wait, press a key
	fmt.Println("done")
}

Observa cómo la ejecución de estas funciones es síncrona

go run rutine.go
0 1 2 3 4 a b c d e f
done

Como implementar Goroutine

Si deseas invocar estas funciones en una Goroutine debes usar “go” por delante de la llamada a la función de esta manera:

func main() {
	// goroutine
	go numbers()

	go characters()

	fmt.Scanln() // wait, press a key
	fmt.Println("done")
}

En la salida se ve como se van ejecutando ambas funciones de manera simultánea, lo que evidencia esta ejecución en paralelo de ambas rutinas.

go run rutine.go
0 a 1 2 b 3 c 4 d e f

Conclusión:

Cuando ejecutas este código sin Goroutine vemos que las funciones se ejecutan una primero y luego la otra, sincrónicamente.
Utilizando Goroutine, anteponiendo “go” por delante a la llamada, puedes observar que la salida de las funciones se va intercalando mostrando que se ejecutan al mismo tiempo.

Compartir esto:

Java 8 Stream map flatMap y filter

Stream map te permite leer cada elemento y transformar los valores que se encuentran en el Stream entregandote otro Stream con el resultado para seguir realizando operaciones.

Cómo usar Stream map

Como puedes ver en el ejemplo a continuación map() recibe una función como parámetro para realizar alguna transformación sobre cada elemento y devuelve otro Stream sobre el cual puedes seguir trabajando.

Observa en el ejemplo a continuación transformamos cada elemento en un String en mayúscula y luego lo colocamos en una nueva lista.

List<String> fruits = Arrays.asList(
    "apple", "banana", "cherry", "lemon", "peach");

List<String> fruitsUpper = fruits.stream().map(String::toUpperCase)
    .collect(Collectors.toList());
System.out.print(fruitsUpper);
// [APPLE, BANANA, CHERRY, LEMON, PEACH]

Otro ejemplo en donde el resultado será las tres primeras letras de cada String.

List<String> fruitsFirstLetters= fruits.stream().map(f -> f.substring(0,3)).collect(Collectors.toList());
System.out.println(fruitsFirstLetters);
// [app, ban, che, lem, pea]

Podemos realizar transformaciones de objetos en algo distinto, recibiendo en la lambda expresión el objeto como parámetro y haciendo algo con él. Por ejemplo, extraer valor de un atributo de un objeto.

List<User> users = Arrays.asList(new User("Hamilton"), new User("Thompson"),
       new User("Stallman"), new User("Torvalds"));

List<String> stringNames = users.stream().map(user -> user.getName()).map(String::toUpperCase)
       .collect(Collectors.toList());
System.out.println(stringNames);
// [HAMILTON, THOMPSON, STALLMAN, TORVALDS]

Cómo usar Stream flatMap

Podemos encontrarnos con estructuras algo más complejas. Por ejemplo, una lista con otra
lista.

List<List<String>> programmers = Arrays.asList(
       Arrays.asList("Margaret", "Hamilton"),
       Arrays.asList("Ken", "Thompson"),
       Arrays.asList("Richard", "Stallman"),
       Arrays.asList("Linus", "Torvalds"));

Si obtienes un Stream de esta lista obtendrías otro Stream con una lista

Stream<List<String>> streamWithList = programmers.stream();

Con flatMap lo que haces es un “flatten”, aplanar el resultado. Por ejemplo, aquí aplanamos List y nos quedamos con un Stream “plano”.

{ {"Margaret","Hamilton"}, {"Ken","Thompson"}, ... } -> flatMap -> {"Margaret","Hamilton","Ken","Thompson", ...}
List<String> flatList = programmers.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(flatList);
// [Margaret, Hamilton, Ken, Thompson, Richard, Stallman, Linus, Torvalds]

Cómo usar Stream filter

Filter aplica un predicado sobre cada elemento y si cumple la condición lo agrega a un nuevo Stream.
Aquí transformas un Stream en otro nuevo Stream con solo elementos que empiezan con “b”

Stream<String> streamWithB = fruits.stream().filter(f -> f.startsWith("b"));
List<String> listWithB = streamWithB.collect(Collectors.toList());
System.out.println(listWithB);
// [banana]

Conclusión

Has visto cómo crear un Stream y transformar cada dato del Stream en algo distinto usando map. También cómo aplanar un Stream complejo para obtener un Stream plano y sobre él seguir realizando operaciones.
Por último, cómo filtrar resultados aplicando un predicado para los elementos que cumplan con tu condición.

Código de ejemplo:
/j8/streams

Compartir esto:

Java 8 Variable Scope en Lambda

Variable Scope en Lambdas

El acceso a variables dentro de lambdas puede ser algo confuso, pero es sencillo si lo ves con un ejemplo.

Observa este código en el cual accedemos a los parámetros “text” y “count” desde la expresión lambda.

public static void showText(String text, int count) {
   Runnable r = () -> {
       for (int i = 0; i < count; i++) {
           System.out.println(text);
       }
   };
   new Thread(r).start();
}

Cuando llamamos al método showText este ejecutará un nuevo hilo con la expresión lambda y retornará de inmediato. ¿Cómo es que estas variables no se pierden dentro de la expresión lambda? Decimos que la expresión lambda ha capturado estos valores de las variables.

Estas variables son llamadas variables libres porque no son parámetros de la expresión lambda en sí misma y no están tampoco definidas dentro del código lambda.

Una expresión lambda tiene tres ingredientes:
-El bloque de código
-Parámetros
-Valores de variables libres.

Restricciones sobre variables en Lambdas

El valor de estas variables libres puede ser capturado por la lambda expresión pero tienen la restricción de que estás variables NO pueden ser modificadas dentro de la expresión lambda.

En nuestro ejemplo no podríamos modificar el valor de la variable “count”. Piensa que si diferentes trabajos ejecutan este código de forma concurrente estarían todos modificando esta contador “count”. Debemos asegurar la expresión lambda siempre “threadsafe”.

public static void showText_notWork(String text, int count) {
   Runnable r = () -> {
       while (count > 0) {
           System.out.println(text + " " + count);
           count--; // Error.  Can't change !!
       }
   };
   new Thread(r).start();
}

Dijimos que NO podemos modificar una variable compartida pero observa que si podemos modificar el valor dentro de un objeto compartido. No tendremos problemas de compilación pero nuestro código no será threadsafe por lo que el resultado puede ser impredecible si múltiples ejecuciones ocurren de forma concurrente.

En este ejemplo la asignación de la variable “data” nunca la estamos cambiando pero si estamos modificando su contenido.

    public static void repeatMessage_noThreadSafe() {
        List<String> data = new ArrayList<>();
        data.add("file1");
        // more into data...
        for (String p : data) {
            new Thread(() -> {
                if (p.startsWith("file9")) {
                    data.add("xfile9"); // changing shared object
                }
            }).start();
        }
    }

Conclusión:

Vimos una breve explicación sobre el scope de las variables en expresiones lambda y porqué no es posible modificarlas.

Código de ejemplo:
/j8/variableScope

Compartir esto:

Java 8 Constructor por Referencia

Java Constructor por Referencia

En Java 8 se han introducido importantes cambios como lambdas, interfaces funcionales y métodos por referencia.

Veamos ahora cómo hacer uso de constructores por referencia.

Si no has leido el articulo sobre métodos por referencia te recomiendo que le des un vistazo porque tiene mucho en común con el artículo a continuación. La diferencia es que aquí llamas al “new” en vez de llamar a un método.

Partamos de un ejemplo para que se vea claramente.

Tenemos este User con tres constructores, uno por default, otro que recibe el “name” y el último que recibe “name” y “password”. Deseamos crear varios user partiendo de una lista de nombres.

public class User {

   private String name;
   private String password;

   public User() {
   }

   public User(String name) {
       this.name = name;
   }

   public User(String name, String pass) {
       this.name = name;
       this.password = pass;
   }
//...

La lista de nombres predefinidos que queremos utilizar para crear los User.

List<String> userNames = new ArrayList<>();
userNames.add("jose");
userNames.add("luis");
userNames.add("lucas");

Como llamar al constructor por referencia

Para este ejemplo nos valdremos de stream().map() que nos permite convertir un objeto en algo diferente. En nuestro “mapeamos” un String en un User.
Luego usaremos stream.collect para recoger todos los elementos en una lista de Users.
Por último los iteramos para mostrar el resultado.

Lo importante aquí es observar que estamos llamando al constructor por referencia cuando escribimos User::new.
¿Pero cuál constructor llamará .map(..) si tenemos varios constructores en la clase User.
Aquí el compilador elige por iferenencia el constructor que tenga un solo parámetro String porque es el único que aplica.

Stream<User> userStream = userNames.stream().map(User::new);
List<User> userList = userStream.collect(Collectors.toList());
userList.forEach(System.out::println);

//.. output..
User{name='jose'}
User{name='luis'}
User{name='lucas'}

En conclusión vemos que así como usamos métodos por referencia tambien podemos hacerlo para constructores del modo User::new. Además el compilador elige el constructor correspondiente por inferencia.

Código de ejemplo:
j8/constructReference

Referencias:
Lambdas
Interfaces funcionales Métodos por referencia

Compartir esto:

Java 8 Optional

Java 8 Optional

Evitar los Nullpointer es uno de los problemas con los que habitualmente nos encontramos los programadores en Java. Continuamente realizamos operaciones para verificar que el valor no sea null o tener la precaución de devolver valores not null y a pesar de esto es habitual el fallo por los null pointer.

Java 8 introduce Optional, este es un wrapper que nos ayuda a prevenir los nullpointer tan comunes en Java. Optional nos da un métodos adecuados para validar si el valor contenido esta null o no.

Java 8 Optional

Como crear un Optional para un objeto

La forma correcta de crear un Optional para un objeto no nulo es la siguiente.

User user = new User("gustavo"); 
Optional<User> optUser = Optional.of(user);

Si utilizas Optional.of con un valor null obtendras un nullpointer inmediatamente en el metodo of.
Esto resulta en la ventaja de no esperar hasta usar el User para encontrarse con la excepción.

Por ejemplo, aqui tendremos un null pointer en Optional, antes de hacer uso de User nulo.

User user = null; 
Optional<User> optUser = Optional.of(user); // NullPointerException here
… 
user.getName(); 

// ...
Exception in thread "main" java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.<init>(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)
	at j8.optional.OptionalExample.main(OptionalExample.java:9)

Si sabes que el objeto User está null o es posible que este nulo y quieres devolver un Optional debes utilizar el método Optional.ofNullable(..);

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

Como utilizar Optional en Java

Dijimos que el uso de Optional es determinar si un objeto está nul o no, aunque es mejor decir que Optional te dirá si el objeto está presente.

A continuación vas a crear un Optional para un objeto User y vas a determinar si está presente.

   // logic to create User 
   User user = new User("john");

   // Create Optional for User
   Optional<User> optUser = Optional.ofNullable(user);

   if (optUser.isPresent()) {
       System.out.println(optUser.get().getName());
   }

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

}

Tambien puedes utilizar <a href="http://gustavopeiretti.com/java-8-usar-expresiones-lambda/">lambdas</a> con el metodo ifPresent(..)



   User user = new User("john");
   Optional<User> optUser = Optional.ofNullable(user);

   // using j8 lambdas
   optUser.ifPresent(u -> System.out.println(u.getName()));

}

Como utilizar Optional orElse / orElseGet en Java

Lo que hace orElse es devolver el valor si está presente o el valor que se pasa por parámetro si está ausente. Es decir usa un valor DEFAULT si el objeto no esta presente.

User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElse(User.EMPTY_USER).getName());

// output 
// EMPTY_USER

En el caso de orElseGet también se devuelve un valor por default si el valor no está presente, solo que aquí el valor se devuelve utilizando la interfaz funcional Supplier.
Lo positivo aquí es que esta función solo será llamada si el valor no esta presente evitando procesamiento. En nuestro ejemplo es bastante simple, pero en los casos en que la ejecución de la función sea costosa este método es recomendable.

// orElseGet
User user = null;
Optional<User> optUser = Optional.ofNullable(user);

System.out.println(optUser.orElseGet(new Supplier<User>() {
   @Override
   public User get() {
       return User.EMPTY_USER;
   }
}).getName());

// better with j8 lambdas !
System.out.println(optUser.orElseGet(() -> User.EMPTY_USER).getName());

Tirando una expection con Optional.orElseThrow

Vimos que usando orElse / orElseGet lo que buscas es devolver otro valor por default si el valor dentro de Optional está ausente. Usando orElseThrow lo que deseas en cambio es tirar una Exception si este valor no está.

// orElseThrow
User nullUser = null;
Optional<User> optUser = Optional.ofNullable(nullUser);
optUser.orElseThrow(IllegalArgumentException::new);

// output
Exception in thread "main" java.lang.IllegalArgumentException
	at java.util.Optional.orElseThrow(Optional.java:290)
	at j8.optional.OptionalExample.main(OptionalExample.java:35)

Como usar Optional.filter en Java

Optional.filter te permite filtrar elementos que no deseas. Suponiendo que solo deseas imprimir algún resultado para un determinado User, en este ejemplo el User cuyo nombre es “john”.

User user = new User("john");
Optional<User> optUser = Optional.ofNullable(user);

// filter + ifPresent
optUser.filter(u -> "john".equals(u.getName()))
       .ifPresent(u -> System.out.println(u.getName()));

// filter + isPresent
if (optUser.filter(u -> "john".equals(u.getName()))
       .isPresent()) {
   System.out.println("john is present");
}


Como usar Optional.map y Optional.flatMap

Optional.map transforma o ‘mapea’ un valor en otro devolviendote otro Optional para el tipo de dato.

Por ejemplo si deseamos obtener un Optional con la edad Integer) del User

Optional<Integer> userAge = optUser.map(User::getAge);

Ahora siendo el valor devuelto con “Optional.map” también otro Optional podemos aplicarle filtros

// filter + map
if(optUser.map(User::getAge).filter(age -> age >= 18).isPresent()) {
   System.out.print("The user is older");
}

¿Cuál es la diferencia entre map y flatMap?

Debes usar map si la función te retorna un objeto, en cambio si la función retorna un Optional usas flatMap

// filter + flatMap
optUser.flatMap(User::getOptionalEmail).ifPresent(email -> sendEmail(email));

private static void  sendEmail(String email) {
   System.out.println("Sending email to " + email);
}


Siempre es conveniente utilizar Optional en vez de validar los nulos en la forma tradicional
Optional nos provee de herramientas para evitar los típicos errores con nulos, aunque es necesario usarlo correctamente porque si no validamos antes que el objeto este presente podemos incurrir facilmente en otro tipo de error.

Si olvidamos verificar si el objeto está presente y hacemos uso de get directamente tendremos una exception.

String data = null;
Optional<String> opt = Optional.ofNullable(data);
opt.get().toString();

Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.util.Optional.get(Optional.java:135)

Optional, además de facilitarnos el control de nulos o no presentes nos provee de funcionalidades como filter, map, orElse, orElseThrow que nos resuelven muchas de las tareas habituales. Puedes consultar este ejemplo en github, espero te sirva.

Compartir esto:

Java 8 como usar fechas y horas con la api java.time

Java 8 nos trae al fin una nueva api para el manejo de fechas. Nos encontramos dentro del paquete
java.time con nuevas clases para resolver los problemas con fechas como LocalDate, horas con LocalTime o la combinación de fecha y hora con LocalDateTime. También incluye como es debido dentro de esta api el uso de zonas horarios con ZonedDateTime.
Además los conceptos de Period para determinar el periodo entre dos fechas y Duration para determinar la duración entre dos horas.

Aquí te mostraré pequeños ejemplos para conocer esta api a fin de que luego en el uso ya te encuentres familiarizado con ellas.

Java 8 Date Time

LocalDate

LocalDate representa la fecha sin la hora.

LocalDate localDate = LocalDate.now();
System.out.println(localDate.toString());

LocalDate localDateOf = LocalDate.of(2017, 10, 10);
System.out.println(localDateOf.toString()); // 2017-10-10

Puedes además sumar o restar días facilmente,

LocalDate datePlus = localDateOf.plusDays(7);
System.out.println(datePlus.toString());  // 2017-10-17

LocalDate dateMinus = localDateOf.minusDays(7);
System.out.println(dateMinus.toString()); // 2017-10-03

Determinar cuál es fecha esta es anterior o posterior respecto a otra

boolean isBefore = LocalDate.of(2017, 10, 10)
                .isBefore(LocalDate.of(2017, 8, 20));
System.out.println(isBefore); // false

boolean isAfter = LocalDate.of(2017, 10, 10)
                .isAfter(LocalDate.of(2017, 8, 20));
System.out.println(isAfter); // true

LocalTime

LocalTime es similar a LocalDate en su uso y representa la hora sin la fecha.

LocalTime localTime = LocalTime.now();
System.out.println(localTime);

LocalTime hour = LocalTime.of(6, 30);
System.out.println(hour); // 06:30

Sumar o restar horas o cualquier otro tipo de unidad como segundos

LocalTime localTimePlus = hour.plus(1, ChronoUnit.HOURS);
System.out.println(localTimePlus); // 07:30
LocalTime localTimeMinus = hour.minus(60, ChronoUnit.SECONDS);
System.out.println(localTimeMinus); // 06:29

También podemos comparar para saber si alguna hora es mayor o no que otra.

boolean isBeforeHour = LocalTime.parse("08:30")
.isBefore(LocalTime.parse("10:20"));
System.out.println(isBeforeHour); // true

LocalDateTime

LocalDateTime es la combinación del la fecha y la hora. Al igual que con LocalDate y LocalTime puedes crear instancias

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
System.out.println(localDateTimeOf); // 2017-08-20T08:30

Igual que como vimos en LocalDate y LocalTime, puedes sumar o restar facilmente utilizando diferentes unidades de tiempo

LocalDateTime localDateTimePlus = localDateTimeOf.plusDays(5);
System.out.println(localDateTimePlus); // 2017-08-25T08:30
LocalDateTime localDateTimeMinus = localDateTimePlus.minusMinutes(10);
System.out.println(localDateTimeMinus); // 2017-08-25T08:20

ZonedDateTime

Si necesitas trabajar con zonas horarias puedes utilizar esta clase que te provee el manejo de fechas con hora para la zona que determines. La lista de zonas disponibles las puedes consultar desde la clase ZoneId

ZoneId.getAvailableZoneIds().forEach(z -> System.out.println(z)); // list of all zones
ZoneId zoneId = ZoneId.of("America/Panama");

Para ‘moverse’ a otra zona horaria, por ejemplo Tokyo haces uso de de LocalDateTime en conjunto con ZoneDateTime pasando la zona “Asia/Tokyo”

LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTimeOf, zoneId);
System.out.println(zonedDateTime); // 2017-08-20T08:30-05:00[America/Panama]
ZonedDateTime tokyoDateTime = localDateTimeOf.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(tokyoDateTime); // 2017-08-20T08:30+09:00[Asia/Tokyo]

Period

Con la clase Period puedes obtener la diferencia entre dos fechas o utilizarlo para modificar valores de alguna fecha.

LocalDate startLocalDate = LocalDate.of(2017, 10, 10);
LocalDate endLocalDate = startLocalDate.plus(Period.ofDays(10));  // 2017-10-20
int diffDays = Period.between(startLocalDate, endLocalDate).getDays();
System.out.println(diffDays); // 10

Duration

Duration es el equivalente a Period pero para las horas.

LocalTime startLocalTime = LocalTime.of(8, 30);
LocalTime endLocalTime = startLocalTime.plus(Duration.ofHours(3));  // 11:30

long diffSeconds = Duration.between(startLocalTime, endLocalTime).getSeconds();
System.out.println(diffSeconds); // 10800 seconds

Código completo

Te dejo copiado todo el ejemplo completo para tu uso. Te recomiendo lo copies y en tu ID para que lo veas más claro. Espero te sirva.

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

public class DateTimeExample {

    public static void main(String[] args) {

        // LocalDate
        localDateExample();

        // LocalTime
        localTimeExample();

        // LocalDateTime
        localDateTimeExample();

        // ZonedDateTime
        zonedDateTimeExample();

        // Period
        periodExample();

        // Duration
        durationExample();

    }


    private static void localDateExample() {
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate.toString());

        LocalDate localDateOf = LocalDate.of(2017, 10, 10);
        System.out.println(localDateOf.toString()); // 2017-10-10

        LocalDate datePlus = localDateOf.plusDays(7);
        System.out.println(datePlus.toString());  // 2017-10-17

        LocalDate dateMinus = localDateOf.minusDays(7);
        System.out.println(dateMinus.toString()); // 2017-10-03

        boolean isBefore = LocalDate.of(2017, 10, 10)
                .isBefore(LocalDate.of(2017, 8, 20));
        System.out.println(isBefore); // false


        boolean isAfter = LocalDate.of(2017, 10, 10)
                .isAfter(LocalDate.of(2017, 8, 20));
        System.out.println(isAfter); // true
    }

    private static void localTimeExample() {
        LocalTime localTime = LocalTime.now();
        System.out.println(localTime);

        LocalTime hour = LocalTime.of(6, 30);
        System.out.println(hour); // 06:30


        LocalTime localTimePlus = hour.plus(1, ChronoUnit.HOURS);
        System.out.println(localTimePlus); // 07:30

        LocalTime localTimeMinus = hour.minus(60, ChronoUnit.SECONDS);
        System.out.println(localTimeMinus); // 06:29

        boolean isBeforeHour = LocalTime.parse("08:30")
                .isBefore(LocalTime.parse("10:20"));
        System.out.println(isBeforeHour); // true
    }

    private static void localDateTimeExample() {
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);


        LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
        System.out.println(localDateTimeOf); // 2017-08-20T08:30


        LocalDateTime localDateTimePlus = localDateTimeOf.plusDays(5);
        System.out.println(localDateTimePlus); // 2017-08-25T08:30

        LocalDateTime localDateTimeMinus = localDateTimePlus.minusMinutes(10);
        System.out.println(localDateTimeMinus); // 2017-08-25T08:20
    }


    private static void zonedDateTimeExample() {
        ZoneId.getAvailableZoneIds().forEach(z -> System.out.println(z)); // list of all zones

        ZoneId zoneId = ZoneId.of("America/Panama");
        LocalDateTime localDateTimeOf = LocalDateTime.of(2017, Month.AUGUST, 20, 8, 30);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTimeOf, zoneId);
        System.out.println(zonedDateTime); // 2017-08-20T08:30-05:00[America/Panama]

        ZonedDateTime tokyoDateTime = localDateTimeOf.atZone(ZoneId.of("Asia/Tokyo"));
        System.out.println(tokyoDateTime); // 2017-08-20T08:30+09:00[Asia/Tokyo]
    }


    private static void periodExample() {
        LocalDate startLocalDate = LocalDate.of(2017, 10, 10);
        LocalDate endLocalDate = startLocalDate.plus(Period.ofDays(10));  // 2017-10-20

        int diffDays = Period.between(startLocalDate, endLocalDate).getDays();
        System.out.println(diffDays); // 10
    }

    private static void durationExample() {
        LocalTime startLocalTime = LocalTime.of(8, 30);
        LocalTime endLocalTime = startLocalTime.plus(Duration.ofHours(3));  // 11:30

        long diffSeconds = Duration.between(startLocalTime, endLocalTime).getSeconds();
        System.out.println(diffSeconds); // 10800 seconds
    }

}

Compartir esto:

Java – como usar ExecutorService

Java ExecutorService

ExecutorService nos simplifica las tareas asincronas proveyendo un pool de hilos que son manejados por esta API abstrayendonos del trabajo de crearlos y asignarles tareas a estos hilos.

java executor service

Utilizando la factory class Executors

La forma más común para crear una instancia de ExecutorService es a través de la factory class Executors.

Por ejemplo si deseas crear pool con 10 threads lo puedes hacer de este modo:

ExecutorService executor = Executors.newFixedThreadPool(10);

Si quieres crear un pool con un solo hilo en los que las tareas queden en cola y se ejecuten de forma secuencial:

ExecutorService executor = Executors.newSingleThreadExecutor();

Creando instancias directamente

Puedes directamente crear instancias de cualquiera de las implementaciones de ExecutorService, observa que esta es una interfaz con múltiples implementaciones.

Por ejemplo para crear un pool de 10 hilos del mismo modo que lo hace la factory class Executors.newSingleThreadExecutor().

ExecutorService executor = new ThreadPoolExecutor(10, 10,
       0L, TimeUnit.MILLISECONDS,
       new LinkedBlockingQueue<Runnable>());

¿Cómo ejecutar tareas con ExecutorService?

Usando ExecutorService.execute

ExecutorService en su método execute ejecuta cualquier implementacion de Runnable, por lo que su ejecución la haces de este modo:

Runnable command = new Runnable() {
   @Override
   public void run() {
       doLongWork();
   }
};
executor.execute(command);

Usando lambdas puedes cambiar este código por uno más limpio. Copia este codigo completo y pruebalo localmente.

package executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceExample {

   public static void main(String[] args) {

       ExecutorService executor = Executors.newFixedThreadPool(10);
       executor.execute(() -> doLongWork("hi 1"));
       executor.execute(() -> doLongWork("hi 2"));
       executor.execute(() -> doLongWork("hi 3"));
   }

   private static void doLongWork(String hola) {
       System.out.println("Running " + hola);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }

}

Usando ExecutorService.submit

EjecutorService.execute es un método void por lo que no puedes conocer el resultado, pero ExecutorService puede también retornar el resultado de tu ejecución si utilizas el método submit.

ExecutorService executor = Executors.newFixedThreadPool(10);
Future<String> future = executor.submit(() -> doLongWork("hi with future 1"));
future.get();

Future.get te devuelve el resultado de la ejecución de tu metodo luego de que este ya ha terminado.

Prueba este ejemplo completo

package executor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceFutureExample {

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

       ExecutorService executor = Executors.newFixedThreadPool(10);
       Future<String> future = executor.submit(() -> doLongWork("hi with future 1"));
       System.out.println(future.get());
      
   }

   private static String doLongWork(String msg) {
       System.out.println("Running " + msg);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "done " + msg;
   }

}

Usando ExecutorService.invokeAny

ExecutorService.invokeAny recibe una lista de Callable, ejecuta todas las task y devuelve el resultado de una ejecución correcta

ExecutorService executor = Executors.newFixedThreadPool(10);

Collection<Callable<String>> callables = new ArrayList<>();
callables.add(() -> doLongWork("hi! 1"));
callables.add(() -> doLongWork("hi! 2"));
String oneResult = executor.invokeAny(callables);

Usando ExecutorService.invokeAll

ExecutorService.invokeAll recibe una también una lista de Callable, ejecuta todas las task y pero devuelve el resultado de todas las ejecuciones

package executor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceFutureAnyExample {

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

       ExecutorService executor = Executors.newFixedThreadPool(10);

       Collection<Callable<String>> callables = new ArrayList<>();
       callables.add(() -> doLongWork("hi! 1"));
       callables.add(() -> doLongWork("hi! 2"));

       List<Future<String>> result = executor.invokeAll(callables);

       for (Future f : result) {
           System.out.println("f.isDone :  " + f.isDone() + " f.get : " + f.get());
       }

   }

   private static String doLongWork(String msg) {
       System.out.println("Running " + msg);
       try {
           Thread.sleep(1000l);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "finish " + msg;
   }

}

Como terminar ExecutorService

Por defecto ExecutorService se queda ‘escuchando’ nuevas tareas, se queda vivo para recibir nuevas tareas y asignarlas a su pool. Si deseas finalizarlo para que no reciba nuevas tareas puedes ejecutar su método shutdown() para “solicitarle” la terminación. Una vez terminadas la tareas en ejecución se finalizará el servicio.
También puedes requerir la finalización inmediata usando shutdownNow(), aunque esto último no esta garantizado. Este método te devuelve la lista de tareas que no finalizaron si deseas conocerlas.

ExecutorService executor = Executors.newFixedThreadPool(10);
...
List<Runnable> runnableList = executor.shutdownNow();

Compartir esto:

Go Tipos Básicos

Go Types – Go Tipos básicos

¿Cuáles son los tipos básicos definidos en Go?

Go define diferentes tipos básicos entre los que puedes encontrar booleanos, string, numéricos enteros (con signo o sin signo), flotantes (números reales) y números complejos.

Este recuadro te muestra los tipos básicos definidos en Go

Tipo booleano:
bool

Tipo string:
string


Enteros con signo:
int         32-bit y 64-bit según el sistema
int8        8-bit (-128 to 127)
int16       16-bit (-32768 to 32767)
int32       32-bit (-2147483648 to 2147483647)
int64       64-bit (-9223372036854775808 to 9223372036854775807)

Enteros sin signo:
uint        32-bit y 64-bit según el sistema
uint8       8-bit (0 to 255)
uint16      16-bit (0 to 65535)
uint32      32-bit (0 to 4294967295)
uint64      64-bit integers (0 to 18446744073709551615)
uintptr    

Flotantes:
float32     32-bit 
float64     64-bit 

Complejos:
complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte // alias for uint8
rune // alias for int32 represents a Unicode code point

Los tipos int, uint y uintptr suelen tener 32 bits de ancho en sistemas de 32 bits y 64 bits de ancho en sistemas de 64 bits.
En general cuando necesites usar un entero utiliza siempre int a menos que tengas un motivo específico para usar enteros de tamaño definido o sin signo.

package main

import (
	"fmt"
	"math"
)

func main() {

	// Boolean
	var b bool = true
	if (b) {
		fmt.Println("is true")
	}

	// String
	var a string = "Hello World"
	fmt.Println(a)

	// Integer number
	var i int = 100
	fmt.Println(i + 99)

	// Float number
	var f float64 = 1.343435345563459
	fmt.Println(f)

	var fmax = math.MaxFloat64
	fmt.Println(fmax)

	// Complex number
	var c complex64 = 1.04i
	fmt.Println(c)

}

El resultado de este ejemplo

$ go run basictypes.go
is true
Hello World
199
1.343435345563459
1.7976931348623157e+308
(0+1.04i)

Compartir esto:

Go Errors

Go Errors

¿Cómo manejar los errores en Go?

Go utilizar como concepto de error cualquier devolución a través de alguna implementación de la interfaz “error” provista por el lenguaje

type error interface {
      Error() string
}

Para usar esta interfaz y devolver un error puedes utilizar una implementación ya provista por Go sobre esta interfaz haciendo uso de la función errors.New() del paquete errors.

Una vez creado error lo retornas y luego evaluas si es no es nulo.
Por convención el error es el último valor retornado si devuelves más de un valor.

package main

import (
	"fmt"
	"errors"
)

func addTwo(value int) (int, error) {
	if (value < 0) {
		// business rules .. I can not work with negatives
		return value, errors.New(fmt.Sprintf("something went wrong value is %d:", value))
	} else {
		return value + 2, nil
	}
}

func main() {

	v, err := addTwo(-1);
	if err != nil {
		// do something with this error
		fmt.Print(err)
	} else {
		fmt.Print(v)
	}

}


El resultado de este código es

$ go run errors.go
something went wrong value is -1:

Compartir esto:

Go Punteros

Go Punteros (Pointers)

¿Qué son los punteros en Go?

Un puntero es una referencia al valor almacenado en memoria.

Para obtener la referencia al puntero del valor debes usar el símbolo “&” por delante y para obtener la desreferencia usas el símbolo “*

Por defecto Go envia los argumentos como valor, si deseas que los argumentos sean enviados por referencia debes usar un puntero.

Hagamos una prueba. Crea dos métodos, el primero “addOne” que reciba un valor entero al cual le sumas un valor. Este argumento será recibido por valor. No se usan punteros aquí.

func addOne(value int) {
      value = value + 1
}

El segundo método a crear es “addOnePointer” que haga lo mismo pero que reciba el argumento como como puntero para desreferenciarlo de su posición de memoria a la variable. Este argumento será recibido como referencia.

func addOnePointer(value *int) {
      *value = *value + 1
}

Haciendo una prueba simple puedes observar que en el primer método “func addOne(value int) “ el argumento se recibe por valor como una copia por lo cual la variable original no es modificada.

En el segundo método func addOnePointer(value *int) en donde indicamos usar el puntero, el valor se recibe por referencia y la variable original si es modifica, ya que estamos accediendo a la posición de memoria y modificandola allí.

package main

import "fmt"

// value param
func addOne(value int) {
      value = value + 1
}

// pointer param, desreference param
func addOnePointer(value *int) {
      *value = *value + 1
}

func main() {
      i := 1
      fmt.Println("Value of i is:", i)

      addOne(i)
      fmt.Println("addOne:", i)

      // using memory address of i (pointer)
      addOnePointer(&i)
      fmt.Println("addOnePointer:", i)

}

El resultado de la prueba es el siguiente:

go run pointers.go
Value of i is: 1
addOne: 1
addOnePointer: 2

Compartir esto:

Go Interfaces

Go Interfaces

¿Qué son las interfaces en Go?

En Go una Interface es un conjunto o colección de métodos que deberán ser implementados.

Por ejemplo, suponemos que deseas una interfaz para figuras geométricas IShape que defina un método de cálculo del área para todos los tipos de figuras

type IShape interface {
      area() float64
}

Ahora lo que haces es implementar esa interface que calcula el área para diferentes figuras.

Defines un struct Rectangle, Square y Triangle con sus campos:

// Create a Rectangle struct
type Rectangle struct {
      Lenght float64
      Width  float64
}

// Create a Square struct
type Square struct {
      SideLength float64
}

// Create a Triangle struct
type Triangle struct {
      Base   float64
      Height float64
}

Implementas el método de la interfaz para cada uno. Observa que la implementación de la interfaz está implícita. Si vienes de Java como es mi caso esto puede resultar confuso; en Java debes explícitamente indicar que tu clase implementa determinada interfaz.

El método tiene un receptor para Rectangle y el nombre del método es el de la interfaz, por lo que implícitamente estamos implementando para Rectangle el método de la interfaz. Lo mismo para el resto de figuras Square y Triangle.

// Implement IShape.area() for Rectangle
func (r Rectangle) area() float64 {
      return r.Lenght * r.Width
}

// Implement IShape.area() for Square
func (s Square) area() float64 {
      return s.SideLength * 2
}

// Implement IShape.area() for Triangle
func (t Triangle) area() float64 {
      return t.Base * t.Height / 2
}

Creas una función que reciba la interfaz y muestre y devuelva el resultado

func ShowArea(s IShape) string {
      return fmt.Sprintf("Area %f", s.area())
}

Tu código completo para este ejemplo luce así:

package main

import (
      "fmt"
)


// Interface
type IShape interface {
      area() float64
}

// Create a Rectangle struct
type Rectangle struct {
      Lenght float64
      Width  float64
}

// Create a Square struct
type Square struct {
      SideLength float64
}

// Create a Triangle struct
type Triangle struct {
      Base   float64
      Height float64
}

// Implement IShape.area() for Rectangle
func (r Rectangle) area() float64 {
      return r.Lenght * r.Width
}

// Implement IShape.area() for Square
func (s Square) area() float64 {
      return s.SideLength * 2
}

// Implement IShape.area() for Triangle
func (t Triangle) area() float64 {
      return t.Base * t.Height / 2
}

// Function to show Area
func ShowArea(s IShape) string {
      return fmt.Sprintf("Area %f", s.area())
}

func main() {
      // creating a Rectangle
      fmt.Println("Rectangle", ShowArea(Rectangle{10, 50}))
      fmt.Println("Square", ShowArea(Square{40}))
      fmt.Println("Triangle", ShowArea(Triangle{20,30}))
}

Ejecutando tu código:

$ go run interfaces.go
Rectangle Area 500.000000
Square Area 80.000000
Triangle Area 300.000000

Compartir esto:

Golang Packages

Go Packages

¿Cómo organiza Go sus archivos?

Go organiza sus programas en paquetes y se inicia su ejecución en el package main

package main

func main() {
      print("Hola, Mundo!")
}
$ go run packages.go
Hola, Mundo!

Go, por convención, usa como nombre de package el último elemento de la ruta de los import.

Por ejemplo, si importamos el paquete “math / rand”, esto incluye todos los archivos que se encuentran en el paquete rand.

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("Random number", rand.Intn(10))
}

Compartir esto: