Podemos definir en Go nuevos tipos de contenedores con otras propiedades o campos como en otros lenguajes de programación. Por ejemplo, podemos crear el tipo llamado persona
para representar una persona, este tipo tiene nombre y edad. Podemos llamar estos tipos de tipos como struct
.
type persona struct {
nombre string
edad int
}
Mira que fácil es definir un struct
!
Tiene dos campos.
nombre
es unastring
usada para guardar el nombre de personas.edad
es unint
usado para guardar la de edad de personas.
Vamos a ver como usarlo.
type persona struct {
nombre string
edad int
}
var P persona // p es de tipo persona
P.nombre = "Astaxie" // asigna "Astaxie" al campo 'nombre' de p
P.edad = 25 // asigna 25 al campo 'edad' de p
fmt.Printf("El nombre de la persona es %s\n", P.name) // accedemos al campo 'nombre' de p
Tenemos tres formas mas de definir un struct.
-
Asignando un valor inicial en forma ordenada
P := persona{"Tom", 25}
-
Usando el formato
campo:valor
para inicializarlo sin ordenP := persona{edad:24, nombre:"Bob"}
-
Definimos una struct anónima, y la inicializamos
P := struct{nombre string; edad int}{"Amy",18}
Vamos a ver un ejemplo completo.
package main
import "fmt"
// definimos un tipo nuevo
type persona struct {
nombre string
edad int
}
// comparamos la edad de dos personas, y devolvemos la mas vieja con la
// diferencia, struct es pasado por valor
func Older(p1, p2 persona) (persona, int) {
if p1.edad>p2.edad {
return p1, p1.edad-p2.edad
}
return p2, p2.edad-p1.edad
}
func main() {
var tom persona
// inicialización
tom.nombre, tom.edad = "Tom", 18
// inicializamos los dos valores con el formato "campo:valor"
bob := persona{edad:25, nombre:"Bob"}
// inicializamos los dos valores en orden
paul := persona{"Paul", 43}
tb_Older, tb_diff := Older(tom, bob)
tp_Older, tp_diff := Older(tom, paul)
bp_Older, bp_diff := Older(bob, paul)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, bob.nombre , tb_Older.nombre , tb_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, paul.nombre, tp_Older.nombre, tp_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", bob.nombre, paul.nombre, bp_Older.nombre, bp_diff)
}
Solo les mostré como definir struct con campos que tienen nombre y tipo. De hecho, Go soporta campos sin nombre pero si con tipo, vamos a llamar a estos campos incrustados.
Cuando el campo incrustado es un struct, todos los campos de ese struct serán campos del nuevo struct de forma implícita.
Vamos a ver un ejemplo.
package main
import "fmt"
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // campo incrustado, esto significa que el struct Student va a incluir los campos que tiene Human.
speciality string
}
func main() {
// inicializamos a student
mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
// campos accesibles
fmt.Println("Su nombre es ", mark.name)
fmt.Println("Su edad es ", mark.age)
fmt.Println("Su peso es ", mark.weight)
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos un campo
mark.speciality = "AI"
fmt.Println("Mark cambio su especialidad")
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos su edad
fmt.Println("Mark esta mas viejo")
mark.age = 46
fmt.Println("Su edad es ", mark.age)
// modificamos su peso
fmt.Println("Mark ya no es mas un atleta")
mark.weight += 60
fmt.Println("Su peso es ", mark.weight)
}
Figure 2.7 Herencia en Student y Human
Vemos que accedemos a la edad y nombre en Student de la misma forma que lo hacemos con Human. Así es como funcionan los campos incrustados. Es muy útil “cool”, no lo es? Espera, hay algo todavía mas “cool”! Puede utilizar a Student para acceder a los campos incrustados de Human!
mark.Human = Human{"Marcus", 55, 220}
mark.Human.age -= 1
Todos los tipos pueden ser utilizados como campos incrustados.
package main
import "fmt"
type Skills []string
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // struct como un campo incrustado
Skills // string como un campo incrustado
int // usamos un tipo embebido como un campo incrustado
speciality string
}
func main() {
// inicializamos al Student Jane
jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
// accedemos a sus campos
fmt.Println("Su nombre es ", jane.name)
fmt.Println("Su edad es , jane.age)
fmt.Println("Su peso es ", jane.weight)
fmt.Println("Su especialidad es ", jane.speciality)
// modificamos el valor del campo skill
jane.Skills = []string{"anatomy"}
fmt.Println("Sus habilidades son ", jane.Skills)
fmt.Println("Ella adquirió don habilidades mas ")
jane.Skills = append(jane.Skills, "physics", "golang")
fmt.Println("Sus habilidades ahora son ", jane.Skills)
// modificamos un campo embebido
jane.int = 3
fmt.Println("Su numero preferido es ", jane.int)
}
En el ejemplo anterior, podemos ver que a todos los tipos se les pueden incrustar campos y podemos utilizar funciones para operarlos.
Hay un problema mas, si Human tiene un campo llamado phone
y Student tiene otro campo llamado con el mismo nombre, que deberíamos hacer?
Go utiliza una forma muy sencilla para resolverlo. Los campos exteriores consiguen accesos superiores, lo que significa, es que cuando se accede a student.phone
, obtendremos el campo llamado phone en student, no en el struct de Human. Esta característica se puede ver simplemente como una sobrecarga
de campos.
package main
import "fmt"
type Human struct {
name string
age int
phone string // Human tiene el campo phone
}
type Employee struct {
Human // campo embebido Human
speciality string
phone string // phone en employee
}
func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
fmt.Println("El teléfono del trabajo de Bob es:", Bob.phone)
// accedemos al campo phone en Human
fmt.Println("El teléfono personal de Bob es:", Bob.Human.phone)
}
- Índice
- Sección anterior: Sentencias de control y funciones
- Siguiente sección: Orientado a objetos