Write a generic max method using a recursive bound, and explain why a plain Comparable bound is insufficient.
answer
- <T extends Comparable<T>> T max(List<T>)
- Body needs item.compareTo(best), both T
- Raw Comparable → compareTo(Object), loses safety
- Comparable<?> won't compile for two Ts
- Library form: Comparable<? super T>
basics
~10 sDeclare <T extends Comparable<T>> T max(List<T> list) and loop calling compareTo. A plain (raw) Comparable bound takes Object in compareTo, so it can't guarantee you're comparing two Ts and you lose type safety.
solid answer
~40 sA correct generic max uses the recursive bound so each element can be compared to another element of the same type. Inside the loop you call item.compareTo(best), and the bound is what makes that call type-safe — both sides are T. A plain Comparable bound (raw) means compareTo(Object), so the compiler can no longer prove the argument is a T; you'd get raw-type warnings and could pass mismatched types at runtime. A wildcard bound (Comparable<?>) is even worse for this purpose: compareTo would accept some unknown type, not T, so item.compareTo(best) won't even compile cleanly. The recursive bound <T extends Comparable<T>> (or the library-grade <T extends Comparable<? super T>>) is the minimal constraint that makes the algorithm both compile and stay type-safe.
code
java · 10 linesstatic <T extends Comparable<T>> T max(List<T> list) {
if (list.isEmpty()) throw new NoSuchElementException();
T best = list.get(0);
for (T item : list)
if (item.compareTo(best) > 0) best = item;
return best;
}
// Library-grade signature also accepts supertype ordering:
// static <T extends Comparable<? super T>> T max(List<T> list) { ... }go deeper
Can write the method with the recursive bound by following the pattern and call compareTo correctly.
Explains why raw Comparable and Comparable<?> are insufficient and handles the empty-list case.
Reaches for Comparable<? super T>, offers a Comparator overload, and discusses compareTo/equals consistency.
Frames the API choice (natural ordering vs. injected Comparator) and the maintainability of exposing recursive bounds to callers.
## Goal Write a method that returns the largest element of a list, generically, with compile-time type safety. ## The working version ```java static <T extends Comparable<T>> T max(List<T> list) { if (list.isEmpty()) throw new NoSuchElementException(); T best = list.get(0); for (T item : list) if (item.compareTo(best) > 0) best = item; return best; } ``` The bound `<T extends Comparable<T>>` declares: "T is some type that can compare itself to a T." That is precisely what the body needs at `item.compareTo(best)` — `item` is `T`, `best` is `T`, and `compareTo` here has signature `compareTo(T)`. ## Why plain `Comparable` (raw) fails ```java static <T extends Comparable> T maxBad(List<T> list) { ... } // raw Comparable ``` `Comparable` without a type argument is a **raw type**. Its `compareTo` degrades to `compareTo(Object)`. Consequences: - The compiler emits unchecked/raw-type warnings. - It no longer guarantees the argument is a `T`; the type system has lost the link between receiver and argument. You can smuggle in mismatched types, getting a `ClassCastException` at runtime instead of a compile error. The recursion is exactly the lost link: it re-binds the comparand to `T`. ## Why `Comparable<?>` fails ```java static <T extends Comparable<?>> T maxAlsoBad(List<T> list) { ... } ``` Here `T` is comparable to **some unknown** type `?`. So `item.compareTo(best)` does not compile cleanly, because `best` (a `T`) is not known to be that unknown type. The wildcard expresses "comparable to something," which is not strong enough to compare two `T`s to each other. ## The library-grade refinement `Collections.max` uses `<T extends Comparable<? super T>>`. This accepts a type whose `compareTo` is declared on a *supertype* (e.g. `Apple` ordered via `Fruit implements Comparable<Fruit>`). It is strictly more permissive than `Comparable<T>` while still type-safe, following PECS (Comparable consumes a `T`, so `? super T`). For an interview, the simple `Comparable<T>` is fine; mention `? super T` to show depth. ## Takeaways - The recursive bound is the *minimal* constraint that makes ordering algorithms compile and stay safe. - Raw `Comparable` and `Comparable<?>` both break the same-type link in different ways. - `Comparable<? super T>` is the production-quality form.
- How would you make max work for types that aren't naturally Comparable?Add an overload taking a Comparator<? super T>: <T> T max(List<T> list, Comparator<? super T> cmp), and call cmp.compare(a, b) instead of compareTo. This decouples ordering from the type and is more flexible.
- What's the difference between compareTo returning 0 and equals returning true?compareTo==0 means 'equal for ordering'; equals means object equality. They're recommended to be consistent but aren't required to agree — e.g. BigDecimal('1.0') and BigDecimal('1.00') compareTo==0 but are not equals.
saying these in an interview costs you the question
- Using raw Comparable and ignoring the warning
- Thinking Comparable<?> is equivalent to Comparable<T> here
- Forgetting the empty-list edge case
- Returning the wrong sign convention from compareTo (negative = less than)