Walking, talking and quacking in Java

Created 16th April, 2007 02:30 (UTC), last edited 18th April, 2007 11:07 (UTC)

This is prompted by something Reg Braithwaite pondered about Java, What does Barbara Liskov have to say about Equality in Java? It's also drawn from a conversation I was having with psykotic on Reddit.

Java is unique as an object oriented language because of its object model and this has important consequences for polymorphism.

Actually polymorphism is one of my favourite subjects when discussing object orientation because its so important and so poorly understood. Even though I am going to talk about Java let's get started with Smalltalk.

bird waddle.
bird quack.

If bird understands the commands waddle and quack then we're justified in calling it a duck, i.e. that it is an instance of some class called Duck. This is what most people know as duck typing and it's a common idea in all sorts of object oriented languages (and actually in all sorts of other types of programming languages too).

So far so good? Actually so far so bad. My explanation is absolutely and totally and one hundred percent wrong.

Duck typing isn't about whether the object is a Duck or not, it's about whether the object waddles and quacks or not.

This form of polymorphism is called operational polymorphism. Personally I find this term makes it clearer what it is that we're really talking about, but actually they both describe the same thing.

We don't actually care what sort of duck bird is. We don't even really care if bird is a goose so long as it does a convincing enough impression of being a duck.

This is what the Liskov Substitution Principle puts into mathematical terms for us¹ [1Although for some reason the paper itself confuses the issue by talking about inheritance.]. The paper describes exactly how convincing that goose needs to be for us to be able to use it as a duck in a variety of different circumstances.

We can see the same principle at work in Javascript:

function pond( bird ) {
    bird.waddle();
    bird.quack();
}

The function pond will work with any birds that we pass it so long as they waddle and quack. I'm labouring the point here, but that's because it's important.

For both the Javascript and the Smalltalk versions what happens if the birds they're given don't waddle and quack? We get a runtime error is what happens.

In C++ it looks a little different, but the effect is exactly the same:

template< typename B >
void pond( B &bird ) {
    bird.waddle();
    bird.quack();
}

Again we can use pond with anything that waddles and quacks, but we've now swapped our runtime error for a compile time one. The reason is that in C++ this sort of duck typing happens in the compiler when the software is built, not at run-time when the software is executed. This limits our use of operational polymorphism in C++ and there are many times we can't use it where we would be able to in a dynamic language.

But back to Java

When James Gosling was designing Java he didn't want to have templates because they complicate the syntax too much. Fair enough, but object oriented idioms are based on operational polymorphism so he had a problem.

The solution comes from thinking a bit more about my fist explanation.

If bird understands the commands waddle and quack then we're justified in calling it a duck, i.e. that it is an instance of some class called Duck.

I said that this was completely wrong, but maybe there is a little something to it anyway? What if we can structure² [2If I understood psykotic properly then operational polymorphism can be considered as a form of structural sub-typing.] some sort of type from the operations we need? What would a solution like that look like?³ [3Actually it might not look like this because it's so long ago since I wrote any Java I've probably got the syntax wrong.]

interface Duck {
    pulbic void waddle();
    public void quack();
}

What this has done is to translate the operational polymorphism into inclusional polymorphism. Inclusional polymorphism is where we talk about a given type as including the sub-classes of the class [4Note that there is a subtle difference in the meaning of the terms “type” and “class” here. The distinction is important, but it's not one I feel I understand well enough to try to explain.If I understand psykotic then this is a form of nominal sub-typing.].

This is the secret of Java interfaces and why they're needed. It's almost impossible to do all sorts of interesting object oriented style things if you don't have operational polymorphism and this meant that he had to add interfaces that could be added to any class.

Of course James had also wanted to leave multiple inheritance out of Java because he felt it was too hard to use properly. This is true, but it's not necessarily much harder than normal, single inheritance. Of course normal inheritance is actually much harder to use properly than it looks, but that's a story for another time. One thing it does do though is to complicate both the compiler and the syntax because of something called the "Diamond problem".

In any case he couldn't have it both ways. In order to have operational polymorphism he had to use either templates or multiple inheritance. In the end he went with multiple inheritance but in a slightly restricted form.

Further reading

As well as the one or two things I've already linked these are worth reading:


Categories:

Discussion for this page