In Java, subtypes and supertypes define relationships between different types in an inheritance hierarchy.
Type substitution refers to the ability to replace a super type with any of its sub type.
List<Integer>).ArrayList<Integer> or LinkedList<Integer>).
When using type substitution, you can only call methods that are declared in the apparent type, even if additional methods exist in the actual type.
List<Integer> myList = new LinkedList<Integer>();
myList.add(5);
myList.size();
// ⛔️ ERROR: `getFirst()` is only in `LinkedList`
myList.getFirst();
LinkedList<Integer> myOtherList = new LinkedList<Integer>();
myOtherList.add(5);
myOtherList.size();
// ✅ OK: `getFirst()` is allowed for `LinkedList`
myOtherList.getFirst();
Variable assignment and method parameter passing follow strict type compatibility rules, ensuring that the assigned value or argument is a sub type of the expected type.
// ✅ OK: `ArrayList` is a subtype of `List`
List<Integer> myList = new ArrayList<>();
// ✅ OK: Same type
ArrayList<Integer> myArrayList = new ArrayList<>();
// ✅ OK: Allowed subtype assigned to super type
myList = myArrayList;
// ⛔️ ERROR: Super type assigned to subtype
myArrayList = myList;
The last assignment fails because
myListhas an apparent type ofList<Integer>, which might hold anyList<Integer>implementation, not necessarily anArrayList<Integer>.
public class CollectionUtil {
void addAll(ArrayList<Integer> list) {
...
}
}
CollectionUtil myCollection = new CollectionUtil();
List<Integer> myList = new ArrayList<>();
ArrayList<Integer> myArrayList = new ArrayList<>();
// ⛔️ ERROR: `List` is not an `ArrayList`
myCollection.addAll(myList);
// ✅ OK: Allowed
myCollection.addAll(myArrayList);
addAll()accepts only anArrayList<Integer>, so passingList<Integer>(even if it's actual type isArrayList<Integer>) fails.
public class CollectionUtil {
List<Integer> makeList() {
// ✅ OK: `ArrayList` is a subtype of `List`
return new ArrayList<>();
}
LinkedList<Integer> makeLinkedList() {
// ⛔️ ERROR: `ArrayList` is not a subtype of `LinkedList`
return new ArrayList<>();
}
}
interface List<E> { ... } // Supertype of ArrayList and LinkedList
class ArrayList<E> implements List<E> { ... } // Subtype of List
class LinkedList<E> implements List<E> { ... } // Subtype of List
List<E> is the supertype of both ArrayList<E> and LinkedList<E>.ArrayList<E> and LinkedList<E> are subtypes of List<E>.