In both C# and Java, methods themselves can be generic:
class ListExtras {
static <T extends Comparable<T>> T max (List<T> ts) {
T res = ts.get(0);
for (T t : ts)
if (res.compareTo(t) < 0)
res = t;
return res;
}
// ...
}
C#/Java-style bounded quantification is enough for this particular use case. My complaint is that bounded quantification in these languages is tied to inheritance as shown by <T extends Comparable<T>>. In Haskell and Rust, bounded quantification is tied to type classes, which, as you said, are open - a type class instance for a given type may be made anywhere in the program, not just where the type is defined. So bounded quantification in Haskell and Rust (and its emulation in Scala via implicits) is strictly more powerful than bounded quantification in C# and Java.As an aside, C# is a little less retarded than Java, so even primitive types like int are objects, and, in particular, int implements IComparable<int>.
Bounded generics do not solve the problem I tried to express. Here's a Scala sample:
Comparable is not a type-class, it's the standard java.lang.Comparable interface. Yet the defined method, "max", only works if the container's generic T implements Comparable[T]. As in ... Of course, Java's standard interfaces are normally useless, so Integers don't implement Comparable ... Note that this restriction is only on max() and not on the generic class-wide definition of T. In Java and C#, you can only do this: Oh, but look, now the whole container can only be used for things that are Comparable and nothing else.