Walking, talking and quacking in Java
Created 2007-04-16T02:30:58.047Z, last edited 2007-04-18T11:07:57.254Z
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 waddle
s and quack
s 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 us1. 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 bird
s 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 bird
s 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 waddle
s and quack
s, 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 commandswaddle
andquack
then we're justified in calling it a duck, i.e. that it is an instance of some class calledDuck
.
I said that this was completely wrong, but maybe there is a little something to it anyway? What if we can structure2 some sort of type from the operations we need? What would a solution like that look like?3
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 class4.
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:
- psykotic explaining duck typing and how it relates to both nominal and structural sub-typing.
- Some slides describing row polymorphism (PDF) which is a more formal explanation of how the process we used to derive the
Duck
interface can be done.
© 2002-2025 Kirit & Tai Sælensminde. All forum posts are copyright their respective authors.
Licensed under a Creative Commons License. Non-commercial use is fine so long as you provide attribution.