Idea: you could fake comparable for things that are not comparable.
We know the two objects are serializable, so we could just do a byte by byte comparison of their serialized representations if they do not implement Comparable... would be slower, would probably not be the comparison result that people expect... but it would be consistent and not error out.
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) {
byte[] b2;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(a);
b2 = bos.toByteArray();
}
for (int i = 0; i < Math.min(b1.length,b2.length); i++) {
int r = Byte.compare(b1[i],b2[i]);
if (r != 0) {
return r;
}
}
return Integer.compare(b1.length,b2.length);
}
is an example of the kind of algorithm. The idea being that all you really want is a consistent comparison because they do not implement Comparable... if somebody had a useful comparison contract for objects of that type then presumably they would already have implemented the comparability contract.
Another approach that could work would be to use Tuple as a builder for ComparableTuple by giving each one a comparable method
public class Tuple2<T0,T1> ... {
...
public ComparableTuple2<T0,T1> comparable(Comparator<? super T0> c0, Comparator<? super T1> c1) {
...
}
...
}
public class ComparableTuple2<T0,T1> extends ComparableTuple {
// notice no bounds on the generic type arguments
...
// naïve storage (reduces GC pressure as the builder is not wasted)
// would need analysis to determine if better to allow the builder allocation
// to be elided by JVM and store the values directly as f0 and f1
private final Tuple2<T0,T1> tuple;
private final Comparator<? super T0> c0;
private final Comparator<? super T1>> c1;
...
}
That way you get ComparableTuple2 can ignore the type bounds on its generic arguments because the instantiation path guarantees a comparison can be made.
ComparableTuple2<User,Page> t = Tuple2.of(a,b).comparable(User::byLastName,Page::byLastAcccessed)
That seems very nice and readable...