Is Go object oriented?

Is Go object oriented?

No , it is not. At least not in the way you’d expect. Go’s official documentation says this:

“ Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).

Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.”

Technically, Go implements some object oriented functionality (e.g interfaces, polymorphism) , but it doesn’t actually implement objects .

What?? You mean a language built in the 21st century, that is gaining so much popularity is not really object oriented?

Yes. However, it does implement something similar, so don't worry. If you come from an OOP language like Java or Python, it won't take long before you're comfortable with Go.

The creators of Go wanted to keep things as simple as possible, and inheritance and polymorphism do tend to complicate matters from time to time.

They didn't really come up with anything new to be honest. They took a concept that is pretty old and modified it a bit.

Structs

Anyone who ever programmed in C has probably heard of structs. But if you're coming from JavaScript, Python or even Java , you probably haven't.

Structs are simmilar to classes, except they don't have methods, and you can't extend them. Then again, they kind of do have methods.. and you kind of can extend them. Ok, before you start cursing and leaving negative comments, time do demonstrate what I mean.

A typical struct is written like this :

type Animal struct {

    latinName string
    continentOfOrigin string
    isExtinct bool

}

Looks a bit familiar? When we define a class in Java, we use a similar syntax, no? Except instead of the type Animal struct we would say class Animal . string and bool are obviously the data types, since we know that Go is a statically typed language.

Now, I said that Go structs don't have methods, but then I sort of changed my mind. Well, here's the Go way of writing a "method" :

func (a Animal) hasLongName() bool {
    if len(a.latinName) > 10 {
        return true
    }
    return false
}

Let me elaborate on the above code snippet. The first keyword func is the way we define any function in Go (think def in Python) . Now this (a Animal) is interesting. It is the function receiver . If a function has a receiver, then it is sort of like a method would be in an OOP language. hasLongName is the name of the function and bool is what data type it will return. A receiver function means we can do something like this:

func main() {

    a := Animal{"Canis familiaris", "Europe", false}
    fmt.Println(a.hasLongName()) 
}

Notice how we use the dot operator , like we would in any OOP language when calling a method? That surely makes things a bit more familiar to Java or Python. Also, notice how we instantiate the struct with the curly braces. In an OOP language, we would probably use paretheses. No new keyword either, but if you're coming from Python you're used to this.

Anyway, this hopefully explains the whole "Go doesn't have methods, but actually it does" ordeal I was rambling about before.

Now, I also said that there is no inheritance, and that is absolutely true. But , there is something similar. It's called composition, and it's also nothing new. Java has a number of design patterns depending directly on this concept. Basically, it's something like this:

// first let's define the Dog struct
type Dog struct {
    Animal
    name   string
    gender string
    size   string
}

// now let's instantiate it in the main function
func main(){
     d := Dog{Animal: Animal{"Canis", "Europe", false}, name: "Fluffy", gender: "Male", size: "Small"}
//(By the way, notice how you can also define a struct with colons ,like here)
    fmt.Println(d.isExtinct)
    fmt.Println(d.name)
    fmt.Println(d.hasLongName())
}

So, what I did was instead of saying something like Dog extends Animal like you would see in Java, I just added Animal as a field , and the Dog type now sort of inherited all of it's fields. This looks relatively simple, right?

There's one thing I would like to stress here. It has to do with a topic I will cover in a separate article, but I feel I should mention it here. The reason is, since you saw how easy it is to write a receiver function, you might be inclined to start writing your own and maybe you'll try something like this:


func (d Dog) changeSize(newSize string) {
    d.size = newSize
}


d := Dog{Animal: Animal{"Canis", "Europe", false}, name: "Fluffy", gender: "Male", size: "Small"}
d.changeSize("Medium")
fmt.Println(d.size)

If you're using a good text editor, it will probably already ask you wtf you are doing. If not, I'll tell you - this snippet of code will not work as intended.

You see, Go is a pass by value language, as opposed to most popular languages you are probably used to which are mostly pass by reference. This means that the d Dog is not going to be good ol' Fluffy that we instantiated a few lines above. It's going to be a new Dog, a sort of clone of Fluffy, but our original Fluffy won't really care.

So how do we actually acknowledge that our original Fluffy has gained a few pounds and is now a medium puppy?

Enter pointers.

Now, like I said, it is a topic that is probably one of the most complex to understand in Go (once again, if you are coming from C, you are probably laughing at me) . So, I won't go into too much detail but for now I'll just share this code and try to explain a bit:


func (d *Dog) changeSize(newSize string) {
    d.size = newSize
}


d := Dog{Animal: Animal{"Canis", "Europe", false}, name: "Fluffy", gender: "Male", size: "Small"}
d.changeSize("Medium")
fmt.Println(d.size)

If you are saying to yourself: "This looks an awful lot like the code that didn't work. " Yes, you are right. Except for the * in front of Dog. Basically it is telling the function that you want to access the values of that particular Dog. Not any clone , but the real Fluffy. For now, it is good to remember it like that. For the future, it is best to wait for my article on pointers, or better yet you can google it now and find a lot of quality articles and videos on the subject .

I hope this clears up as to why Go is not an object oriented language, but also why it isn't that hard to transition from OOP classes to Go structs.

Thanks for reading, and see you in the next one!

Did you find this article valuable?

Support Pavle Djuric by becoming a sponsor. Any amount is appreciated!