Historia del tema:
En Go a menudo surgen situaciones donde los tipos integrados son insuficientes y es necesario definir un tipo de dato propio con métodos para encapsular lógica y ampliar funcionalidad. Esto se logra creando tipos personalizados (type) y métodos (func (r Receiver) MethodName()).
Problema:
Los desarrolladores principiantes a menudo se confunden: ¿cuál es la diferencia entre declarar un nuevo tipo basado en uno existente? ¿Cómo implementar correctamente los métodos? ¿Cómo se resuelve el problema de la copia, la transmisión por valor/puntero? Se cometen errores relacionados con la visibilidad, el receptor del tipo y el trabajo con structs embebidos.
Solución:
Para definir un tipo propio se utiliza la palabra clave type. Los métodos se implementan utilizando el receptor (receiver) — esto es importante para trabajar con interfaces.
Ejemplo de código:
type MyInt int func (m MyInt) Double() int { return int(m) * 2 } // Para estructuras: type User struct { Name string Age int } func (u *User) Birthday() { u.Age++ } var u = User{"Alice", 30} u.Birthday() // Age = 31
Características clave:
¿Los tipos personalizados heredan métodos del tipo base?
No. Si se define type MyInt int, entonces MyInt no tiene métodos de int. Por ejemplo, no funcionará una llamada a String() o otros métodos del tipo base.
¿Se pueden definir métodos para un alias de tipo?
Para un alias (type MyType = ExistingType) no se pueden agregar métodos. Los métodos solo se definen para tipos nuevos (type MyType ExistingType), no para alias.
¿Qué receptor utilizar: puntero o valor?
Si el método debe modificar el objeto, es mejor usar un puntero. El receptor de valor copia la estructura, lo que puede llevar a comportamientos inesperados si, por ejemplo, la estructura contiene campos que son slices y mapas.
Ejemplo de código:
type Counter struct { value int } func (c *Counter) Inc() { c.value++ } func main() { c := Counter{} c.Inc() // solo con puntero el método modificará value }
Un programador creó type MySlice []int y esperaba que los métodos de []int, como append, funcionaran como métodos del tipo MySlice. Al final, se dio cuenta de que no había métodos y no se podía tratar a MySlice como []int directamente.
Ventajas:
Desventajas:
Se definió type Counter int con el método Inc, lo que permitió utilizarlo en varias partes del programa con lógica compartida y sin código redundante.
Ventajas:
Desventajas:
int.