// note
A Common Misconception: Confusing Inheritance-Based Polymorphism for Interface-Based Polymorphism
It's a common mix-up to see runtime polymorphism through a base class and assume it must involve an interface, since "polymorphism" and "interface" often get taught together. They're related but distinct: interfaces are one way to get polymorphism, not the only way. This note walks through a concrete example to make the distinction precise: does the code below use interfaces?
class Animal {
void speak() {
System.out.println("sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("bark");
}
}
class Cat extends Animal {
@Override
void speak() {
System.out.println("meow");
}
}
public class Part1_InheritancePolymorphism {
public static void makeSpeak(Animal a) {
a.speak();
}
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
makeSpeak(myDog);
makeSpeak(myCat);
}
}
The answer, by the Java Language Specification's own terms, is no. There is no interface keyword anywhere in this file, no type in this file implements anything, and the runtime dispatch mechanism the JVM uses for this code is contractually distinct from the one it uses for interfaces. Each of those three claims is verifiable independently below.
1. Syntactically: there is no interface in this file
Java has one keyword that declares an interface: interface. It does not appear in this file. Every type declared here uses class, and every relationship between them uses extends:
Dog extends AnimalCat extends Animal
extends between two class declarations is, by definition, class inheritance — a single-inheritance, base-class-to-subclass relationship. implements is the keyword Java reserves for satisfying an interface contract, and it is absent here. This is not a matter of interpretation; it's what the two keywords mean in the JLS (Java Language Specification §8.1.5, "Superclasses and Subclasses" vs. §9.1, "Interface Declarations").
For contrast, here is what an actual interface-based version of the same behavior looks like — this is the code in Part3_InterfacePolymorphism.java:
interface ISpeaker {
void speak();
}
class InterfaceDog implements ISpeaker {
@Override
public void speak() {
System.out.println("bark");
}
}
The interface keyword, and implements, are the syntactic markers of interface-based polymorphism. Neither appears in Part 1. This alone is sufficient to establish that Part 1 is a class-inheritance example, not an interface example.
2. Semantically: Animal provides a real, inherited implementation
Animal.speak() is not an abstract contract — it has a method body (System.out.println("sound")). Dog and Cat override it, which presupposes there was already something concrete to override. An interface method, absent a default body, has no implementation to override in the first place — implementers supply the body, they do not replace one (JLS §9.4, "Interface Method Declarations").
This matters because it demonstrates the defining feature of inheritance that an interface (without defaults) structurally cannot offer: a subclass can be written to inherit Animal's behavior unchanged, as Part2_InheritanceWithDefaults.java does with sleep(). Part 1 uses @Override specifically to demonstrate that every subclass is replacing a real, pre-existing method body — the inheritance-specific case — not fulfilling an interface method that never had one.
3. At the bytecode level: the JVM itself treats these differently
If the classification were merely a matter of naming convention, the compiled bytecode for makeSpeak(Animal a) and an interface-typed equivalent would be identical. They are not. javap -c on Part 1 shows:
public static void makeSpeak(Animal);
Code:
0: aload_0
1: invokevirtual #7 // Method Animal.speak:()V
4: return
invokevirtual is the JVM bytecode instruction reserved for calls resolved against a class's method table — it is only ever emitted when the compile-time type of the receiver (Animal) is a class, not an interface (JVMS §6.5, invokevirtual). The JVM specification defines a separate instruction, invokeinterface, exclusively for calls made through an interface-typed reference (JVMS §6.5, invokeinterface). Part3_InterfacePolymorphism.java's equivalent method compiles to:
public static void makeSpeak(ISpeaker);
Code:
0: aload_0
1: invokeinterface #7, 1 // InterfaceMethod ISpeaker.speak:()V
6: return
The compiler — independently of whatever anyone calls this code — emits invokevirtual for Part 1 and invokeinterface for Part 3. These are not stylistic variants of the same instruction; invokeinterface carries an extra operand (the argument count, shown as 1 above) specifically because interface method resolution has to search a different table (the itable) than class method resolution (the vtable), since a class may implement arbitrarily many unrelated interfaces but has exactly one superclass chain. The bytecode is proof that the JVM's own compiler and runtime treat "dispatch through a class hierarchy" and "dispatch through an interface" as two distinct mechanisms — and Part 1 unambiguously exercises the former.
Conclusion
By syntax (class/extends, no interface/implements), by semantics (overriding a real inherited method body, not implementing a contract), and by compiled bytecode (invokevirtual, not invokeinterface), Part 1 is an example of inheritance-based polymorphism, not interface-based polymorphism — and the distinction is not cosmetic: Part3_InterfacePolymorphism.java in this same project demonstrates the interface-based version of an equivalent example, and it differs from Part 1 in exactly the ways outlined above — different keywords, different relationship to the base type's method body, and a different JVM instruction. See the project overview for the full four-part comparison this project builds on.
References
- Java Language Specification, SE 21 — §8.1.5, Superclasses and Subclasses (defines
extendsbetween classes) - Java Language Specification, SE 21 — §9.1, Interface Declarations (defines
interfaceandimplements) - Java Language Specification, SE 21 — §9.4, Interface Method Declarations (defines abstract vs.
defaultinterface methods) - Java Virtual Machine Specification, SE 21 — §6.5,
invokevirtual(class-hierarchy method dispatch) - Java Virtual Machine Specification, SE 21 — §6.5,
invokeinterface(interface method dispatch, and why it carries an extra operand)