java - Implementing interface comparators -
say have simple interface
wish comparable
based on feature:
interface organism extends comparable<organism> { string getname(); int getcomplexity(); @override default int compareto(organism other) { return this.getcomplexity() - other.getcomplexity(); } }
each implementing class must return unique complexity 2 instances of class have same complexity , 2 instance of different class have different complexity. natural ordering 'group' instances of classes together.
i want implement interface in class overrides default comparison comparing 2 instance of class within class's group of instances in order. use following pattern:
class bacteria implements organism { enum shape {rod, round, spiral}; private final shape shape; @override public int compareto(organism other) { if (other instanceof bacteria) return this.shape.compareto((bacteria)other.shape); else return organism.super.compareto(other); } }
i'm not particularly happy pattern of code: once set of classes implementing interface becomes large becomes quite complex maintain , requires lots of repeated code , depends on implicit property of 'complexity'. prefer comparator
style of defining order. able implement comparable
in bacteria
using looks like:
return comparator .comparingint(organism::getcomplexity) .thencomparing(bacteria::getshape);
to clear, realise comparators don't work this: designed 1 comparator used across collection, not different comparator depending on each object. mention them here not because potential solution because chaining style of comparators elegant , transparent. i'm interested in whether there's elegant way define compareto
allow different orderings within collection depending on class.
i'm not sure plan put comparator
. since you'd class implement comparable<organism>
, i'll assume you're looking like
class bacteria implements organism { enum shape {rod, round, spiral}; private final shape shape; comparator<organism> comparator = comparator .comparingint(organism::getcomplexity) .thencomparing(bacteria::shape); // illegal @override public int compareto(organism other) { return comparator.compare(this, other); } }
this won't work, because thencomparing
, in context, needs parameter function
operates on organism
, not bacteria
. work around this--i'll leave decide whether elegant enough:
comparator<organism> comparator = comparator .comparingint(organism::getcomplexity) .thencomparing(x -> ((x instanceof bacteria) ? ((bacteria)x).getshape() : shape.rod));
in theory, write own method can convert comparator another. can't use instance.method
notation, use have this:
comparator<organism> comparator = mycomparatorutilities.thencomparingifinstanceof( comparator.comparingint(organism::getcomplexity), bacteria.class, bacteria::getshape);
the following implementation of thencomparingifinstanceof
compiles (and allows code above compile), haven't tried test it:
class mycomparatorutilities { public static <t,u extends t,v extends comparable<? super v>> comparator<t> thencomparingifinstanceof( comparator<t> comparator, class<u> subclass, function<? super u, ? extends v> keyextractor) { return (a, b) -> { int comp = comparator.compare(a, b); if (comp != 0) { return comp; } if (subclass.isinstance(a) && subclass.isinstance(b)) { return keyextractor.apply(subclass.cast(a)) .compareto(keyextractor.apply(subclass.cast(b))); } return 0; }; } }
more: answer comment: no, don't think approach more readable or maintainable. in fact, believe whole design unmaintainable, because it's easy add class cause comparison violate properties of total ordering; i'd looking different design, starting clearer definition of how want ordering work on objects of different classes. "right" way handle comparisons depend on different design.
for similar problems, might stick compareto
approach, unless needed class return comparator
other reason (e.g. there multiple orderings defined organism
s). however, might ways eliminate duplication, if every compareto
if
statement same structure, or that.
Comments
Post a Comment