Java :: Using generics to instantiate an "at runtime" Multi-Comparator - java

I have a MultiComparator class that allows one to combine multiple sorts. For example, the code below creates a sort for Contacts.
// This works
contactList.sort(
new MultiComparator<>(
new ContactComparator(ContactComparator.Sort.CONTACT_NAME),
new ContactComparator(ContactComparator.Sort.ID)
)
);
However, I am needed to create the sort order on the fly at runtime, and I am having trouble figuring out how to do this as the java compiler keeps giving me an error: "Cannot infer arguments (unable to resolve constructor)". For simplicity, I have omitted the logic that would create the "ordered" List of sort fields, but assume that the user could create the order. My issue becomes, how to create the MultiComparator given this ordered List. Any ideas?
List<ContactComparator> orderedContactComparators = new ArrayList(); // assume this was created above
contactList.sort(
// compiler error: "Cannot infer arguments (unable to resolve constructor)
new MultiComparator<>(orderedContactComparators)
);
For reference, the other classes are below...
ContactComparator Class
public class ContactComparator implements Comparator<Contact> {
private Sort currentSort;
private boolean sortAsc;
public enum Sort {
NAME, ADDRESS, ID
}
public ContactComparator() {
currentSort = Sort.NAME;
sortAsc = true;
}
public ContactComparator(Sort sort) {
currentSort = Objects.requireNonNullElse(sort, Sort.NAME);
sortAsc = true;
}
#Override
public int compare(Contact o1, Contact o2){
// Compare logic here
}
}
MultiComparator Class
public class MultiComparator<T> implements Comparator<T> {
private List<Comparator<? super T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
#SafeVarargs
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<? super T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return ObjectUtils.compare(o1.hashCode(), o2.hashCode());
}
#SafeVarargs
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
list.sort(new MultiComparator<T>(comparators));
}
}

Related

Implementing a functional interface via method reference

First I got a class named after my Chinese name
public class Yxj<T> {
private T[] data;
private int size = 0;
private final Comparator<? super T> comparator;
public Yxj(Comparator<? super T> c) {
data= (T[]) new Object[16];
comparator = c;
}
public void addItem(T t){
data[size++] = t;
}
public int sort(){
return comparator.compare(data[0], data[1]);
}
public T[] getData(){
return data;
}
}
in which a Comparator resides,then I defined a Norwich keeping a field order and setter and getter of it, finally there's a method used to implement the compare(T t1,T t2) in Comparator.
public class Norwich {
private int order;
public Norwich(int o) {
order = o;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int compareOrder(Norwich n) {
if (order > n.getOrder()) {
return 2;
} else if (order == n.getOrder()) {
return 0;
} else {
return -3;
}
}
}
then here comes the main method
Yxj<Norwich> norwichYxj = new Yxj<>(Norwich::compareOrder);
norwichYxj.addItem(new Norwich(9));
norwichYxj.addItem(new Norwich(1));
System.out.println(norwichYxj.sort());
so what I'm interested in is that, why does not the method compareOrder keep the same parameters as the compare in Comparator but it can still work correctly?
It is simple. You have passed through the constructor your implementation of the Comparator to be used for comparing.
Yxj<Norwich> norwichYxj = new Yxj<>(Norwich::compareOrder);
Remember Comparator is nothing else than an interface. Since it is a functional interface, it can be represented through a lambda expression or a
method reference (as you did). The way you can pass the Comparator in the full form is as follows. Note the usage of the compareOrder method:
Yxj<Norwich> norwichYxj = new Yxj<>(new Comparator<>() {
#Override
public int compare(Norwich o1, Norwich o2) {
return o1.compareOrder(o2); // usage of compareOrder
}
});
This can be shortened to a lambda expression:
Yxj<Norwich> norwichYxj = new Yxj<>((o1, o2) -> o1.compareOrder(o2));
It can be shortened again to a method reference:
Yxj<Norwich> norwichYxj = new Yxj<>(Norwich::compareOrder);
Now you can see it can be represented in this way though the method compareOrder accepts only one formal parameter. The first parameter of the Comparator#compare method is the one invoking the compareOrder method and the second parameter is the one being passed to the compareOrder method.
Learn more here: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Additionally, the classes you have constructed look a bit odd. Though the other answer doesn't in fact answer your question, it can lead you to a better code: Implementing a functional interface via method reference
class Yxj
The paramter T of your class Yxj should have more restrictions if you want to compare/sort in this class with T then say T must be comparable.
If your T array grows then don't implement your own growing array but use ArrayList instead which does that for you
If you do the first you don't need the Comperator anymore
Your methode sort only sorts the first and second element so you will get problems. If the data is shorter you will get an ArrayIndexOutOfBoundsException if it is longer it won't sort the rest of elements. So with a Collection you could simple use Collections.sort(data);
public class Yxj<T extends Comparable<T>> {
private final List<T> data;
public Yxj() {
this.data = new ArrayList<>();
}
public void addItem(T t){
data.add(t);
}
public void sort(){
Collections.sort(data);
}
public List<T> getData(){
return data;
}
public void print(){
System.out.println(data);
}
}
class Norwich
If you done the above know your Norwich class must implement the Comparable interface so you can compare Norwich instances with the methode compareTo which also will be called each time you or the API ask directly or indirectly to compare to Norwich instances like for sorting ect.
public class Norwich implements Comparable<Norwich> {
private int order;
public Norwich(int o) {
this.order = o;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
#Override
public int compareTo(Norwich other) {
return this.order - other.order;
}
#Override
public String toString() {
return "Norwich{" +
"order=" + order +
'}';
}
}
Main
Done? Perfect, then your main could be looks like this
public static void main(String[] args) {
Yxj<Norwich> norwichYxj = new Yxj<>();
norwichYxj.addItem(new Norwich(9));
norwichYxj.addItem(new Norwich(1));
norwichYxj.sort();
norwichYxj.print();
}

Java Comparing Generics with Comparable<? super T> from another class

I have a "Schema" and "Field" model in which a Field represents a data type and has methods on how to parse it, and the schema is a collection of fields. I am trying to implement generic comparison, however, I can't get the code to compile and I can't figure out the proper generic scopes. How can I get this to work?
class Field<T extends Comparable<? super T>> {
T parse(String val) {
...
}
}
public class Schema {
Map<Integer, Field<?>> fields;
Field<?> getField(int index){ ... }
}
public class Comparison {
public static <T extends Comparable<? super T>> boolean greaterThan(Field<T> f, String val1, String val2) {
// compiles as expected
return f.parse(val1).compareTo(f.parse(val2)) > 0;
}
public static boolean greaterThan2(Field<?> f, String val1, String val2) {
// does not compile -> required capture of ? super capture of ?, provided capture of ?
return f.parse(val2).compareTo(f.parse(val2));
}
}
public class App {
public static void main(String[] args) {
Schema s = ...
// does not compile, required type Field<T>, provided type Field<capture of ?>
Comparison.greaterThan(s.getField(0), "val1", "val2");
// compiles
Comparison.greaterThan2(s.getField(0), "val1","val2");
}
}
Below code compiles in 1.8
import java.util.Map;
class Field<T extends Comparable<? super T>> {
T parse(String val) {
return null;
}
}
class Schema {
Map<Integer, Field<?>> fields;
Field<?> getField(int index){ return null; }
}
class Comparison {
public static <T extends Comparable<? super T>> boolean greaterThan(Field<T> f, String val1, String val2) {
// compiles as expected
return f.parse(val1).compareTo(f.parse(val2)) > 0;
}
}
public class App {
public static void main(String[] args) {
Schema s = new Schema();
//compiles ok
Comparison.greaterThan(s.getField(0), "val1", "val2");
}
}

Implementing map function

I am creating a 'SpecialList' and am require to implement the map function. The list should be lazy, and will only produce values when evaluated.
toString() returns "?" if the value is not yet available; returns the string representation of the value otherwise.
get() called when the contents is needed. If the value is already available, return that value; otherwise, compute the value and return it. The computation should only be done once for the same value.
Here's what I have:
public <U> SpecialList<U> map(Function<T, U> mapper) {
if (!this.isAvailable) {
return new SpecialList<U>(this.supplier);
}
return new SpecialList<U>(mapper, value);
}
// private constructor
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
this.isAvailable = false;
}
However, it is telling me that there's no valid constuctor because T cannot be converted to U.
SpecialList.java:65: error: no suitable constructor found for SpecialList(SpecialList<T>.CachedSupplier<T>)
return new SpecialList<U>(this.supplier);
^
constructor SpecialList.SpecialList(U) is not applicable
(argument mismatch; SpecialList<T>.CachedSupplier<T> cannot be converted to U)
constructor SpecialList.SpecialList(Supplier<U>) is not applicable
(argument mismatch; SpecialList<T>.CachedSupplier<T> cannot be converted to Supplier<U>)
Doesn't the 'U' become a T when returned?
How do I go about solving this? I am still a little unclear about the method level generic types. But I was told that I need to add the < U > for my map method.
Below is my code in full:
class SpecialList<T> {
class CachedSupplier<T> {
private Supplier<? extends T> supplier;
private T value;
boolean isAvailable;
public CachedSupplier(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
public T get() {
if (!isAvailable) {
value = supplier.get();
isAvailable = true;
}
return value;
}
}
private CachedSupplier<T> supplier;
private T value;
boolean isAvailable;
private SpecialList(T value) {
this.value = value;
this.isAvailable = true;
}
private SpecialList(Supplier<T> s) {
this.supplier = new CachedSupplier<T>(s);
this.isAvailable = false;
}
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
this.isAvailable = false;
}
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
this.isAvailable = false;
}
public static <T> SpecialList<T> of(T value) {
return new SpecialList<>(value);
}
public static <T> SpecialList<T> of(Supplier<T> supplier) {
return new SpecialList<>(supplier);
}
public <R> SpecialList<R> map(Function<? super T,? extends R> mapper) {
if (!this.isAvailable) {
return new SpecialList<>(this.supplier);
}
return new SpecialList<R>(mapper, value);
}
public T get() {
if(this.isAvailable) {
return this.value;
} else {
this.value = this.supplier.get();
this.isAvailable = true;
return this.value;
}
}
}
I am still a little confused with Generic Types etc. so please let me know if there's anything odd/I can improve!
Thanks
According to the code you posted, there is a compile-time error in one of the constructors for class SpecialList...
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
this.isAvailable = false;
}
Firstly, in the code you posted, there is no constructor in inner class CachedSupplier that takes a Function parameter, so you need to add one with this signature:
public <R> CachedSupplier(Function<T, R> mapper)
And the second problem with the SpecialList constructor is the lambda expression. Abstract method apply in interface Function has a parameter, which your lambda expression is missing. So the constructor code should be:
private <R> SpecialList(Function<T, R> mapper, T v) {
this.supplier = new CachedSupplier<T>((v) -> mapper.apply(v));
this.isAvailable = false;
}
The problem is your map function is changing the SpecialList type parameter from T to R. But in the fragment highlited by compiler you return still SpecialList.
Things you can do:
If you are not planning to change the type of elements with your map() function, get rid of R, and replace Function<? super T,? extends R> mapper with Function<? super T,? extends T> mapper. However, this way the mapper would be totaly omited, which might not be the desired behaviuor.
Otherwise, you should pass into your SpecialList constructor a combination of the original supplier and new mapper like so:
return new SpecialList<R>(() -> {
T originalValue = this.supplier.get();
return mapper.apply(originalValue);
};
I think you're doing an overkill by checking isAvailable in both, the SpecialList and the CachedSupplier. It seems enough to me to do so in the CachedSupplier and reduce the members of SpecialList to just the supplier (which is, when the value is available, simply a value-getter without additional costly evaluations). And in the constructors you make sure that the supplier is a cached one, when needed (i.e. when you don't have the value yet).
So here's my suggestion. Further comments are directly in the source code (which compiles now without errors or warnings):
class SpecialList<T> {
static class CachedSupplier<R> implements Supplier<R> {
/* R is a priori an idependent parameter. If you call it T again, then you get a warning
* that the original T is hidden within the new nested class (though it wouldn't really matter here),
* as one doesn't need to refer to it. I made it static, as you don't need an instance to define it. */
private Supplier<? extends R> supplier;
private R value;
boolean isAvailable;
public CachedSupplier(Supplier<? extends R> supplier) {
this.supplier = supplier;
this.isAvailable = false;
}
#Override
public R get() {
if (!isAvailable) {
value = supplier.get();
isAvailable = true;
}
return value;
}
}
private Supplier<T> supplier;
private SpecialList(T value) {
/* in this case no lazy evaluation and no caching needed */
this.supplier = () -> value;
}
private SpecialList(Supplier<T> s) {
this.supplier = new CachedSupplier<T>(s);
}
private SpecialList(CachedSupplier<T> s) {
this.supplier = s;
}
private <R> SpecialList(Function<R, T> mapper, R v) {
/* This constructor is not needed anymore in my suggested code.
* I left it simply, in order to show how to fix the type errors related to it.
* if I understood correctly the intentions of this constructor,
* R and T have to be swapped w.r.t the original post.
* This is a constructor that uses an R-value and a mapper R->T to create a SpecialList<T> */
this.supplier = new CachedSupplier<T>(() -> mapper.apply(v));
}
public static <T> SpecialList<T> of(T value) {
return new SpecialList<>(value);
}
public static <T> SpecialList<T> of(Supplier<T> supplier) {
return new SpecialList<>(supplier);
}
public <R> SpecialList<R> map(Function<T, R> mapper) {
/* mapper is here different (opposite direction) than in the last of the constructors.
* Here we have an existing SpecialList containing a value of Type T which will be turned into a
* SpecialList containing a value of type R by applying a mapper T->R after getting the value from
* the original SpecialList */
return new SpecialList<R>(() -> mapper.apply(get()));
}
public T get() {
return this.supplier.get();
}
}
This solution doesn't yet take care of the toString() implementation. When you want to implement also the latter, it might be more natural to make SpecialList a subclass of CachedSupplier (with the additional map- and toString- methods and direct access to isAvailable)

How do you refer to nested types using generics in Java?

How do you create a generic class that refers to nested generic types?
I'm trying to create a Comparator class which can compare the inner types of B without wanting to expose what those types are. In the following example I get a compiler warning for raw casting my T inner nested values to Comparable:
public class SSCCE {
// Compare my A instances.
class AComparator<T extends B> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
AComparator<A> comparator = sscce.new AComparator<>();
comparator.compare(sscce.new A(), sscce.new A());
}
}
Is it possible to represent the inner values using to safely allow casting?
Things I've tried:
Creating a wildcard comparable (uncompilable) :
class AComparator2<T extends B<? extends Comparable<?>>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
Comparable<?> o1value = (Comparable) o1.getValue();
Comparable<?> o2value = (Comparable) o2.getValue();
return o1value.compareTo(o2value);
}
}
Declaring a secondary generic parameter type (U), which simply postpones the problem:
class AComparator3<T extends B<U>, U extends Comparable<U>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
U o1value = o1.getValue();
U o2value = o2.getValue();
return o1value.compareTo(o2value);
}
}
...
AComparator3<A, Comparable<U>> comparator = sscce.new AComparator3();
This comparator isn't to compare two instances of the classes A, rather part of their contents.
The wildcard solution does not work
class AComparator2<T extends B<?>> {
public int compare(T o1, T o2)
because T is too loose here; we can't make sure two T's can compare to each other -- it's possible that o1 is a B<X1> and o2 is a B<X2>, and X1, X2 are two different types.
Your 3rd solution restricts T to a specific B<U>
class AComparator3<T extends B<U>, U extends Comparable<U>>
this works perfectly; except that the use site has to specify U, even though U is deducible from T.
AComparator3<A, Integer>
^^^^^^^ duh!
This is annoying. The same problem has been asked before from other use cases. No good answers.
Fortunately, in your case, U isn't needed anywhere on use site, therefore we could simply use a wildcard for it
AComparator3<A, ?> comparator = sscce.new AComparator3<>();
comparator.compare(sscce.new A(), sscce.new A());
In fact, the comparator is a Comparator<A>, which is probably all you need. Also we can create a convenience method to hide the ugliness of new. So you may do something like
Comparator<A> comparator = sscce.comparator();
Have you consider Java 8 solution?
Comparator<A> comparator = ((t1,t2)-> t1.getValue().compareTo(t1.getValue()));
comparator.compare(sscce.new A(), sscce.new A());
You may be interested in comparator which should compare types extending B but only if they hold same comparable type. Such comparator may look like
class AComparator<T extends Comparable<T>> implements Comparator<B<T>> {
#Override
public int compare(final B<T> o1, final B<T> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
and you can use it like
AComparator<Integer> comparator = sscce.new AComparator<>();
comparator.compare(sscce.new A(), sscce.new A());
comparator.compare(sscce.new A(), sscce.new A2());//compilation error
Another option is to have B implement Comparable directly, since you are using getValue() to do the compare. The below gets rid of the warning:
import java.util.Comparator;
public class SSCCE {
class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
abstract class B<T extends Comparable<T>> implements Comparable<B<T>>{
abstract T getValue();
#Override
public int compareTo(B<T> other)
{
return getValue().compareTo(other.getValue());
}
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
Comparator.naturalOrder().compare(sscce.new A(), sscce.new A());
}
}
There are a few things you have to change to achieve what you want, which I believe if just implement a Generic Comparator.
First, AComparator should look like:
// Compare my A instances.
class AComparator<T extends Comparable<T>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.compareTo(o2);
}
}
You don't need your class B, since A and A2 will implement Comparable directly. Just delete it.
Your A and A2 classes:
class A implements Comparable<A> {
#Override public int compareTo(A other) {
// your compare logic here
// return negative if less than, 0 if equal, positive if greater than
}
}
class A2 implements Comparable<A2> {
#Override public int compareTo(A2 other) {
// your compare logic here
// return negative if less than, 0 if equal, positive if greater than
}
}
It is important that you read the documentation for Comparable, to understand what is expected from the returned value.
Does that makes sense?
PS: I didn't test those codes, they are just out of my head.
I guess this is what you want:
public class SSCCE {
static class BComparator<E extends Comparable<E>> implements Comparator<B<E>> {
#Override
public int compare(final B<E> o1, final B<E> o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
static class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
static class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
static abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
BComparator<Integer> comparator = new BComparator<>();
comparator.compare(new A(), new A());
BComparator<String> comparator2 = new BComparator<>();
comparator2.compare(new A2(), new A2());
}
}
If you don't want your comparator to be able to compare instances of two different subclasses of B (like A2 extends B<String> and A3 extends B<String>), the following works:
public class SSCCE {
static class BComparator<E extends Comparable<E>, T extends B<E>> implements Comparator<T> {
#Override
public int compare(final T o1, final T o2) {
return o1.getValue().compareTo(o2.getValue());
}
}
static class A extends B<Integer> {
#Override Integer getValue() { return 1; }
}
static class A2 extends B<String> {
#Override String getValue() { return "Test String!"; }
}
static abstract class B<T extends Comparable<T>> {
abstract T getValue();
}
public static void main(String[] args) {
SSCCE sscce = new SSCCE();
BComparator<Integer, A> comparator = new BComparator<>();
comparator.compare(new A(), new A());
BComparator<String, A2> comparator2 = new BComparator<>();
comparator2.compare(new A2(), new A2());
}
}

Generic class as argument

I have a generic class Card . Rank is interface
class Card<T extends Enum<T> & Rank>
I am trying to create two static comparators of Card.
public final static Comparator<Card<?>> comparatorByAttribute1 = new Comparator<Card<?>>() {
#Override
public int compare(Card<?> o1, Card<?> o2)
{
...
}
};
How can I define that the type of o1 should be the same with o2 ?
Why not just use the actual type in the type declaration?
public final static Comparator<Card<ActualType>> comparatorByAttribute1 =
new Comparator<Card<ActualType>>() {
#Override
public int compare(Card<ActualType> o1, Card<ActualType> o2) {
return 0;
}
};
With...
public enum ActualType implements Rank {...}
Alternatively, if you want to keep the generic type <T>, you will need to resort to using a generic static method, because there is no way of having generic attributes in Java:
public final static <T extends Enum<T> & Rank> Comparator<Card<T>>
comparatorByAttribute1() {
return new Comparator<Card<T>>() {
#Override
public int compare(Card<T> o1, Card<T> o2) {
return 0;
}
};
}
Or, you resort to unsafe casting:
#SuppressWarnings({ "rawtypes", "unchecked" })
public final static <T extends Enum<T> & Rank> Comparator<Card<T>>
comparatorByAttribute1() {
// Your attribute
return (Comparator) comparatorByAttribute1;
}

Categories

Resources