// 01 · fundamentos
Fundamentos y toolchain
Instala una sola cosa, aprende la sintaxis en una tarde y deja que gofmt discuta el estilo por ti.
// un binario que lo hace todo
Toolchain: instala una cosa y ya
Olvídate de instalar JDK + Maven + tu suite de plugins. En Go instalas un binario (go) y ese binario compila, ejecuta, testea, formatea, gestiona dependencias, documenta y detecta race conditions.
# Windows (tu caso), macOS y Linux:
winget install GoLang.Go # o: brew install go / sudo apt install golang
$ go version
go version go1.24.5 windows/amd64
# Un proyecto nuevo son dos comandos. Sin arquetipos, sin spring initializr:
$ mkdir hola; cd hola
$ go mod init github.com/daniel/hola
$ go run . # compila Y ejecuta en un pasoLos comandos que usarás a diario
| Comando | Qué hace | Tu equivalente mental |
|---|---|---|
go run . | Compila y ejecuta al vuelo | mvn spring-boot:run, npm run dev |
go build | Genera el binario nativo | mvn package (pero sin JVM después) |
go test ./... | Corre todos los tests del módulo | mvn test, pytest |
go get pkg@v1.2.3 | Añade/actualiza una dependencia | npm install pkg, editar el pom |
go install pkg@latest | Instala una herramienta CLI global | npm install -g, pipx install |
go fmt ./... | Formatea todo el código (estilo único oficial) | Prettier, pero sin archivo de config |
go vet ./... | Análisis estático de errores comunes | SonarLint / ESLint básico |
go doc fmt.Println | Documentación en la terminal | Javadoc, pero instantáneo |
go test -race | Detector de condiciones de carrera | No existe equivalente cómodo en Java |
go mod tidy | Ordena dependencias (añade/borra) | mvn dependency:analyze que además arregla |
Tu editorVS Code + extensión oficial Go (usa el language server gopls): autocompletado, refactors, test runner y debug con delve. Si vienes de IntelliJ, GoLand de JetBrains te resultará casa. Para hot-reload tipo nodemon/Spring DevTools: la herramienta air.
Variables de entorno y cross-compiling (GOOS, GOARCH, GOPATH…)
go env muestra la configuración. Las que importan:
GOPATH— carpeta de caché de módulos y binarios instalados (~/go). Desde los módulos (2019) ya no trabajas dentro de GOPATH; tu proyecto vive donde quieras. Si un tutorial habla deGOPATH/src, ciérralo: está obsoleto.GOOS/GOARCH— sistema y arquitectura destino. Compilar desde tu Windows para un servidor Linux es una línea:GOOS=linux GOARCH=amd64 go build. Sin toolchains cruzados, sin Docker para compilar.CGO_ENABLED=0— desactiva el puente a C () y garantiza un binario 100% estático, ideal para contenedoresFROM scratch.GOTOOLCHAIN— desde 1.21 el propiogopuede descargar y usar la versión que pida elgo.mod: adiós a "en mi máquina compila".
// lo justo para leer cualquier código Go
Sintaxis esencial
Todo archivo Go pertenece a un paquete. El paquete main con una función main() es el punto de entrada — como tu public static void main, sin la liturgia.
package main
import "fmt" // la stdlib se importa por ruta; lo no usado NO compila
func main() {
fmt.Println("Hola desde Go") // sin punto y coma: los pone el compilador
}Variables, constantes y zero values
Dos formas de declarar: var (explícita) y := (inferencia, solo dentro de funciones). Toda variable no inicializada tiene un — en Go no existe la no-inicialización.
package main
import "fmt"
const Pi = 3.14159 // const sin tipo: se adapta al contexto (más flexible que final)
// iota genera secuencias: el enum minimalista de Go
const (
Pendiente = iota // 0
EnCurso // 1
Hecha // 2
)
func main() {
var edad int // 0 ← zero value, nunca "basura" ni null
var nombre string // "" ← string vacío, NO nil
var activo bool // false
precio := 19.99 // float64 inferido (:= solo dentro de funciones)
x, y := 10, 20 // asignación múltiple, como Python
fmt.Println(edad, nombre, activo, precio, x+y)
}Trampa para javeros: shadowing con :=Dentro de un if o for, err := ... crea una variable nueva que tapa a la exterior. Es el bug clásico del que viene de Java. go vet y el linter avisan — hazles caso.
Funciones: retornos múltiples, el superpoder cotidiano
Las funciones son valores de primera clase (como en JS/Python) y pueden devolver varios valores sin tuplas ni wrappers. De aquí nace el patrón de errores de Go y el patrón valor, ok.
// Retorno múltiple: adiós Optional<T>, adiós tuplas artificiales
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("división entre cero")
}
return a / b, nil
}
// Variádicas, como los varargs de Java
func sumar(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
resultado, err := dividir(10, 3)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(resultado)
// Closures: idénticas a las de JavaScript que usas en React
contador := 0
incrementar := func() int {
contador++
return contador
}
fmt.Println(incrementar(), incrementar()) // 1 2
fmt.Println(sumar(1, 2, 3, 4)) // 10
}Tipos básicos frente a Java
| Go | Java | Nota |
|---|---|---|
int, int64 | int, long | int es de 64 bits en máquinas modernas; sin autoboxing, sin Integer |
float64 | double | El default para decimales; float32 existe pero es raro |
string | String | Inmutable, UTF-8 nativo; se compara con == (¡sí, de verdad!) |
rune | char-ish | Un code point Unicode (int32); byte es un octeto crudo |
bool | boolean | Sin conversión implícita: if 1 {} no compila (a diferencia de Python) |
| no existe | null | Los tipos valor nunca son nil; solo punteros, slices, maps, canales e interfaces |
Conversiones: en Go todo casting es explícito, siempre
Java convierte int → long solo. Go, jamás: var l int64 = int64(unInt). Entre números y strings se usa el paquete strconv:
strconv.Itoa(42)→"42"(comoString.valueOf)strconv.Atoi("42")→42, nil(devuelve error si no parsea, sin NumberFormatException)fmt.Sprintf("%d-%s", 7, "go")→ tuString.formatde siempre
Molesta dos días. Después descubres que nunca más depuraste una conversión implícita sorpresa.
// un solo bucle para gobernarlos a todos
Control de flujo y defer
Go tiene un único bucle: for. Hace de for, while, do-while y for-each. Y el switch no necesita break — no hay fall-through accidental.
// 1. for clásico
for i := 0; i < 5; i++ { fmt.Println(i) }
// 2. for como while
for saldo > 0 { saldo -= pago }
// 3. for infinito (tu while(true))
for { procesarSiguiente() }
// 4. for range: el for-each universal (slices, maps, strings, canales)
for i, fruta := range []string{"mango", "kiwi"} {
fmt.Println(i, fruta)
}
// 5. range sobre enteros (Go 1.22+): como range(5) de Python
for i := range 5 { fmt.Println(i) }
// if con sentencia de inicialización: el err vive SOLO dentro del if
if err := guardar(usuario); err != nil {
return err
}
// switch sin break (rompe solo), acepta condiciones tipo if/else-if
switch {
case hora < 12:
fmt.Println("buenos días")
case hora < 19:
fmt.Println("buenas tardes")
default:
fmt.Println("buenas noches")
}defer: tu try-with-resources, pero mejor
defer pospone una llamada hasta que la función termina — por cualquier camino, incluso con panic. Es el idioma estándar para liberar recursos, y sustituye a tu try-with-resources de Java y al with de Python.
func procesarArchivo(ruta string) error {
f, err := os.Open(ruta)
if err != nil {
return err
}
defer f.Close() // se ejecuta al salir de la función, pase lo que pase
// ... trabajar con f sin preocuparte de cerrarlo en cada return ...
return nil
}
// Los defer se apilan en orden LIFO (último en entrar, primero en salir)
func demo() {
defer fmt.Println("3º")
defer fmt.Println("2º")
fmt.Println("1º")
} // imprime: 1º, 2º, 3ºNo hay operador ternariox = cond ? a : b no existe. Se escribe con if/else normal. Es la queja #1 de todo recién llegado y la decisión es final: los autores consideran que los ternarios anidados son ilegibles.
// nuevo · el acento nativo
Convenciones de nombres y godoc
En Go, las convenciones no son sugerencias de un wiki: la visibilidad depende de la primera letra y las herramientas oficiales asumen el resto. Escribir "con acento Java" es la señal #1 de recién llegado.
| Regla | Bien ✓ | Mal ✗ (acento Java/Python) |
|---|---|---|
| MixedCaps siempre | maxRetries, ParseURL | max_retries, MAX_RETRIES |
| Acrónimos en bloque | userID, servirHTTP | userId, servirHttp |
| Nombres cortos cerca de su uso | for i, u := range usuarios | indiceActualDeUsuario |
| Sin prefijo del paquete | http.Client, bytes.Buffer | http.HTTPClient (se leería http.HTTPClient) |
| Interfaces de un método: -er | Reader, Notificador | IUserService, UserServiceInterface |
| Sin getters con "Get" | u.Nombre() o campo público | u.GetNombre() |
Errores: Err… / …Error | ErrNoEncontrado, *ValidationError | NOT_FOUND_EXCEPTION |
Comentarios de documentación
Un comentario justo encima de una declaración exportada es su documentación oficial: go doc y pkg.go.dev lo publican tal cual. La convención: empieza con el nombre de lo que documenta.
// Paquete precios calcula descuentos y totales con impuestos.
// (comentario sobre "package": documenta el paquete entero)
package precios
// AplicarDescuento devuelve el precio tras aplicar el cupón.
// Un cupón desconocido no es un error: simplemente no descuenta.
func AplicarDescuento(precio float64, cupon string) float64 {
// ...
}Comparado con JavadocSin @param, sin @return, sin HTML: prosa simple en una o dos frases. La firma tipada ya documenta los parámetros; el comentario explica lo que la firma no puede decir. Si escribes párrafos de Javadoc, estás sobre-documentando.