Interfaces and Inheritance
Java supports two ways to share behavior across classes: inheritance (extending a class) and interfaces (implementing a contract). Knowing when to reach for each is core to writing idiomatic Java.
Inheritance
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public String speak() { return name + " makes a sound"; }
}
public class Dog extends Animal {
public Dog(String name) { super(name); } // call the parent constructor
@Override
public String speak() {
return name + " barks";
}
}
Dog extends Animal inherits its fields and methods, then overrides speak() with its own version. super(name) calls Animal's constructor, since Dog doesn't own the name field itself, it inherited it.
Interfaces
An interface declares what a class must do, without saying how:
public interface Comparable2<T> {
int compareTo(T other);
}
public class Student implements Comparable2<Student> {
int xp;
public int compareTo(Student other) {
return this.xp - other.xp;
}
}
A class can implements as many interfaces as it wants, but can only extends one class, this is a deliberate Java design decision to avoid the ambiguity of multiple inheritance ("if two parent classes both define speak() differently, which one wins?").
Why prefer interfaces?
Code written against an interface (List<String> instead of ArrayList<String>) doesn't care which concrete implementation it's handed, ArrayList, LinkedList, anything, as long as it implements List. This is called programming to an interface, and it's one of the most valuable habits in all of object-oriented design.