Structures
About
Go isn't a object-oriented language like Java or C#. It doesn't have objects, inheritance, polymorphism nor overloading.
What it does have are structures, which can be associated with methods.
type Foo struct {
Foo string
Bar string
}
Functions on Structures
func (f *Foobar) Baz() {
s.Bar = "Baz"
}
In the above code, we say that the type *Foobar
is the receiver of the Baz
method.
foobar := &Boobar{"Foo", "Bar"}
foobar.Baz()
fmt.Println(foobar.Bar) // Baz
Constructors
Go doesn't have a constructor method like other programming languages like Java do.
Instead, we create a function that returns an instance of the type.
func NewFoobar(foo string, bar string) *Foobar {
return &Foobar{
Foo: foo,
Bar: bar,
}
}
Our function doesn't need to return a pointer. Below is also valid.
func NewFoobar(foo string, bar string) Foobar {
return Foobar{
Foo: foo,
Bar: bar,
}
}
Go has a build-in new
function which is used to allocate the memory required by a type.
The result of new(X)
is the same as &X{}
:
foobar := new(Foobar)
foobar := &Foobar{} // same
But the latter is often preferred because we can specify fields to initialize in a more readable way.
foobar := new(Foobar)
foobar.Foo = "foo"
foobar.Bar = "bar"
// v.s.
foobar := &Foobar {
Foo: "foo",
Bar: "bar",
}
Composition
Go supports composition: the act of including one structure into another.
This is called a trait or a mixin in some languages.
For example, Java doesn't have an explicit composition mechanism, so a mixin would be written like this:
public class Person {
private String name;
public String getName() {
return this.name;
}
}
public class Saiyan {
// Saiyan is said to have a person
private Person person;
// we forward the call to person
public String getName() {
return this.person.getName();
}
// ...
}
This can be tedious because every method of Person
needs to be duplicated in Saiyan
.
Go avoids tediousness.
type Person struct {
Name string
}
func (p *Person) Introduce() {
fmt.Printf("Hi, I'm %s\n", p.Name)
}
type Saiyan struct {
*Person
Power int
}
goku := &Saiyan{
Person: &Person{"Goku"},
Power: 9001,
}
goku.Introduce()
// The compiler did give it a field name.
// Both are perfectly valid.
fmt.Println(goku.Name)
fmt.Println(goku.Person.Name)
Overloading
Go doesn't support overloading. You'll see and write a lot of functions like Load
, LoadById
, LoadByName
and so on.
However, because implicit composition is really just a compiler trick, we can "overwrite" the functions of a composed type.
For example, our Saiyan
structure can have its own Introduct
function.
func (s *Saiyan) Introduce() {
fmt.Printf("Hi, I'm %s. Ya!\n", s.Name)
}
The composed version is always available via s.Person.Introduce()
.
goku.Introduce() // Hi, I'm Goku. Ya!
goku.Person.Introduce() // Hi, I'm Goku
REF
Many examples here are excerpted from The Little Go Book.
Last updated
Was this helpful?