IMHO Interfaces are a match nicer alternative to multiple inheritance.
Interfaces provide away to define very general behavior that is applicable to more then one group of objects. This allows you to define exactly how a given Class will implement that behaviour, either at a supper class level or at an indervidual subclass level.
Now you could do this as you defined, but it wouldnt be very polymorphic. You would end up with objects inheriting from Tyre having one set of behaviours and the ones inheriting from Ball with another, and all Ball's must override bounce, but BeanbagBall dosnt bounce
Interfaces on the other hand allow for polymorphic behaviour, in that you could have a collection of type Bounceable, and you would be able to just call the doBounce method on each member of the collection. Where as without the Bounceable interface (just using inheritance) your collection of things you wish to test bounce will be of type Object, which dosnt have a doBounce method, so you would have to test each object with instanceof, and then cast to the appriate type.
Of course you could make Tyre and Ball have a common super class, perhaps called RubberItem this might be ok, until you need to give bounceable behaviour to something that isint a RubberItem, such as a Spring, should a Spring inherit from a rubberItem?
Interfaces can also be used as "markers", in that you do NOT provide any methods to be implemented, but your objects will still pass the IS-A test for the Interface.
Sorry if that is as clear as mud, just my thoughts on the subject.
We use interfaces to define the behavior shared by non-related classes (i.e. classes that does not have is-a or has-a relationship). Whereas super class, abstract class will be used when the classes are related to each other.
As Rorry mentioned, the different ball types might be related to each other so as the tyres. But, if the tyres and ball need to share a common behavior, interface should be considerable.
interfaces don't give anything to the person implementing them. they help the person who is going to USE those classes.
a collection can only take a single type of object. I can therefore write my collection to accept Bounce objects. Now, it doesn't matter if that object is a Ball or a Tyre, i can stick them all in my collection, iterate through it, and call jump() on all the objects in there.
In fact, a year later, I can be on the beach sipping a drink when my boss calls. Our vendor is changing their "Tyre" class to a "Tire" class.
I don't care, because they will still be implementing the Bounce interface, so MY CODE WORKS.
Meanwhile, you, who wrote your code to expect a Tyre object, have to pack up your stuff, go into the office, and re-write your code to now accept a Tire object. you need to re-compile, re-test, and re-distribute your code to your 5000 customers all around the world.
There are only two hard things in computer science: cache invalidation, naming things, and off-by-one errors