Go favors composition over inheritance. There are no classes; you model behavior with methods on types, interfaces satisfied implicitly, and embedding for reuse.
type Dog struct {
Name string
Age int
}
d := Dog{Name: "Buddy", Age: 3}
d.Age = 4
A method is a function with a receiver. Use a pointer receiver *Dog when you mutate the receiver or want to avoid copying large structs.
func (d Dog) Bark() string {
return d.Name + " says woof!"
}
func (d *Dog) Birthday() {
d.Age++
}
Go has no built-in constructors; use a factory function returning a value or pointer.
func NewDog(name string, age int) *Dog {
return &Dog{Name: name, Age: age}
}
Embed a type anonymously inside a struct to promote its fields and methods.
type Animal struct{ Name string }
func (a Animal) Speak() string { return "..." }
type Dog struct {
Animal // embedded
Breed string
}
d := Dog{Animal: Animal{Name: "Buddy"}, Breed: "Retriever"}
_ = d.Name // promoted field
Method promotion follows lookup rules; if Dog defines Speak, it overrides for Dog receivers.
An interface is a set of method signatures. A type satisfies an interface if it implements those methods — no implements keyword.
type Speaker interface {
Speak() string
}
func Announce(s Speaker) {
println(s.Speak())
}
The empty interface interface{} or any (since Go 1.18) accepts any value.
type Reader interface{ Read([]byte) (int, error) }
type Writer interface{ Write([]byte) (int, error) }
type ReadWriter interface {
Reader
Writer
}
Use small interfaces at the call site (io.Reader, fmt.Stringer) so many types can plug in.
Capitalized identifiers are exported (public from other packages). Lowercase names are package-private.
*T.Prefer explicit fields when the relationship is not “is-a”; use embedding sparingly for real extension points.