CSE224 - Go OOP

Class

Named Type

I can create a new named type MyInt that is based on int, but is treated as a distinct type by the compiler.

1
type MyInt int

struct + methods

Go doesn’t have traditional classes, instead, it uses Custom types + attached methods. As a result, structs in go can hold both data and associated methods. Example:

1
2
3
4
5
6
7
8
9
10
11
type Celsius float64

// This defines a method on the Celsius type.
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32 // need type convertion
}

func main() {
temp := Celsius(25)
fmt.Println("In Fahrenheit:", temp.ToFahrenheit()) // 77
}

Another example: User behaves just like a class

1
2
3
4
5
6
7
8
9
type User struct {
Name string
Age int
}

// This function is like a method defined in a class
func (u User) Greet() string {
return "Hello, " + u.Name
}

Constructors

Go does not have built-in constructors. We define a function by ourself as a constructor:

1
2
3
func NewUser(name string, age int) User {
return User{Name: name, Age: age}
}

Encapsulation

Go doesn’t use public or private keywords like Java or C++. Instead, it uses capitalization to control visibility.

1
2
3
4
type Account struct {
Owner string // Exported, other packages can read/write it.
balance float64 // Unexported, only code inside this package can access it.
}

Composition

Composition over Inheritance: Go doesn’t have classes or inheritance, but it has composition using embedding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Animal struct {
Name string
}

func (a Animal) Speak() {
fmt.Println(a.Name, "makes a sound")
}

type Dog struct {
Animal // embedded
Breed string
}

// Now Dog inherits the Speak() method from Animal.
d := Dog{Animal: Animal{Name: "Buddy"}, Breed: "Labrador"}
d.Speak() // Buddy makes a sound

By composition, I can resue methods.

Override

1
2
3
4
// Define a method with the same name on the outer struct, and it will shadow the embedded (override)
func (d Dog) Speak() {
fmt.Println(d.Name, "barks!")
}

Interface (Polymorphism and Abstraction)

An interface in Go is a set of method signatures. If a type has those methods, it automatically implements the interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Greeter interface {
Greet() string
}

type Person struct{}

// Person has a Greet() method, so it automatically satisfies the Greeter interface.
func (p Person) Greet() string {
return "Hello!"
}

func sayHello(g Greeter) {
// any type that has Greet() can be passed as a parameter (Polymorphism)
fmt.Println(g.Greet())
}

func main() {
p := Person{}
sayHello(p) // Hello!
}

If the method is defined on a pointer receiver, only the pointer type implements the interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Greeter interface {
Greet()
}

type Person struct {
Name string
}

// This means only *Person (pointer) has the method Greet()
func (p *Person) Greet() {
fmt.Println("Hello, my name is", p.Name)
}

func main() {
var g Greeter

p := Person{Name: "Alice"}

// g = p // ❌ error (p is a Person (value))
g = &p // ✅ &p is a *Person
g.Greet()
}

In this example, Person doesn’t implement the interface, but *Person does, so assigning a Person to a Greeter causes an error. (g = &p works)


CSE224 - Go OOP
https://thiefcat.github.io/2025/05/30/CSE224/go-oop/
Author
小贼猫
Posted on
May 30, 2025
Licensed under