What are “types” in OOP, really

When I google the phrase “types in Java” or “types in OOP”, I am presented with a list of the primitive data types that have been a part of most programming languages forever. However, in an Object Oriented context, the word “type” refers to something a bit more abstract and a bit more interesting than simply storage space.

Right side, left side.

Consider the standard idiom to instantiate an object in C# or Java.

Object o = new Object();

Notice that unlike say python or javascript, we write the word “Object” twice. The question then is, why do we do so? What is the difference between the word “Object” on the left side and the word “Object” on the right side.

The technically correct answer is that the word on the right side is a Constructor and that on the left side is the name of the class and you’d be right for saying that. However, what is on the right is not simply the class name, it is also the name of the type.

The word “type” does not actually refer to the “code” of the class, i.e. the body of the class. It does not refer to the exact definition or implementation of a class or it’s functions somewhere. The word “type” only refers to the public members of a class. It does not include the private or protected members.

This boils down to the concept that the collection of all the operations you can do on an object (i.e. the functions you can call on it, etc.) form the type of the object. This collection can also be known as the interface of the class, i.e. how someone can use the class.

Using a Class

Consider the following situation. Fred and Bill are working on some kind of coffee order system where Fred designs the frontend and Bill designs the classes and the database i.e. the backend. For those who may not know, here are some of the various kinds of coffee you can make:

Types_coffee_chart

Bill defines a Coffee class that provides methods to add Espresso (i.e. the  base of the coffee) and options to add other ingredients to make whatever kind of coffee a user needs. Perhaps this coffee class looks like the following:


class Coffee {
    private void grindCoffee() {
        // ...
    }
    private void addCoffee() {
        // ...
    }
    public void addWater() {
        // ...
    }
    public void addEspresso() {
        grindCoffee();
        addCoffee();
        addWater();
        // return 30ml espresso
    }
    public void addFoam(int qty) {
        //.. add some amount of foam
    }
    public void addSteamedMilk(int qty) {
        //.. add some amount of milk
    }
}

The interface of the Coffee class would include addEspresso(), addFoam() and addSteamedMilk() and Fred can combine these to make whatever kind of coffee they need and offer it in their user interface. This is how Fred will use the class Coffee.

Fred of course cannot control how the Espresso is made. They are only presented with the option to add the Espresso by the Coffee class. The functions to grind and add coffee are used internally by the class and therefore they are private.

So the addEspresso(), addFoam() and addSteamedMilk() functions would make up the type of the Coffee object. For dynamically typed languages like Python or Ruby, this “type” is decided at runtime, i.e. when the code is actually executing. For statically typed languages, the “type” i.e. the collection of methods that you can call on an object is set at compile time. This is why a grindCoffee() operation on a Coffee object would fail because the compiler would not be able to find that method in the object’s type, since it is private.

As long as the function is present in the object’s type (i.e. the left side in our idiom above) the function call will work. This is where Inheritance and Polymorphism come into play.

Inheriting Types

The type of an object does not tell you anything about how the function works, i.e. how it is implemented. It just tells you that you can call that function on that object. As long as the function is present in the type of the object, it doesn’t really matter how it is written. Consider the following example:

class A {
    public void foo() {
        System.out.println("A");
    }
}

class AB extends A {
    @Override
    public void foo() {
        System.out.println("AB");
    }
    public void bar() {

    }
}

If we make an object of type A and point it to a reference of class AB with:

A obj = new AB()

then obj.foo() will call foo() from AB. This is basic Polymorphism. What’s interesting however, is that obj does not have a bar() method because that isn’t part of the type A.

If we want obj to change types, we’ll have to cast it appropriately. However, at compile time, the type of obj i.e. the list of methods and stuff that can be performed on it does not contain bar(). Of course this does not mean that when we define an unrelated class with the same methods as A, we can do the same thing because a type is not literally only the method list. However, any class that is part of A‘s hierarchy i.e. any class down the inheritance structure can be used to create an object of type A.

This fact actually helps in decoupling a lot of the code that you may write. It is also what makes Dependency Injection possible.

Interfaces in Java (and C#) can also be used in the same way as we used A. An interface really is just a type declaration if you think about it. Consider a class C implementing an interface called Iface

We can actually do the following:

Iface obj = new C();

This works the same way for any class implementing Iface even down the inheritance tree, i.e. in this case, any subclass of C.

The beauty of this of course is that developers can make accurate assumptions about the type of the object coming in from somewhere and not have to worry about the implementation.

Decoupling code

If we look at the Coffee Order system example above, if Fred who’s working on the frontend only uses Interface, Bill can modify and extend his classes on his side without breaking things for Fred.

For Example, if Fred has a simple method called makeCappuccino(), he could define it somewhat like this:

void makeCappucino(CoffeeType obj) {
    obj.addEspresso();
    obj.addFoam();
    obj.addSteamedMilk();
}

Now it doesn’t matter if Bill sends Coffee or any special subclass of Coffee, say FancyCoffee or something. As long as it derives from the CoffeeType interface, Fred can use it to make a cappuccino, making his code independent of Bill’s code in this regard.

I hope this clears up some doubts people might’ve had about the word “type” in OOP and hopefully reading this will help in some way.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s