Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Class hierarchies with Generics, Comparator and sort error

I have the following classes:

  public interface ISort<T> {
      public List<T> sort(List<T> initialList);
  }


  public abstract class Sort<T> implements ISort<T> {
    private Comparator<? super T> comparator;
   
    public Sort(Comparator<? super T> comparator) {
        this.comparator = comparator;
    }

    @Override
    public List<T> sort(List<T> initialList) {
        ArrayList<T> list = new ArrayList<T>(initialList);
        Collections.sort(list, comparator);
   
        return list;
    }
  }


public abstract class InternalTreeItem<T> {   
    public abstract String getValue();
}

public class D extends InternalTreeItem<Integer> {
   private Integer i;
   
   public D(Integer i) {
       this.i = i;
   }
   
   @Override
   public String getValue() {
       return i.toString();
   }
   
   public Integer getInteger() {
       return i;
   }
}

public class DComparator implements Comparator<D> {
    @Override
    public int compare(D o1, D o2) {
        return o1.getInteger() - o2.getInteger();
    }
}

public class DSort extends Sort<D> {
    public DSort(Comparator<D> comparator) {
        super(comparator);
    }
   
    public DSort() {
        super(new DComparator());
    }
}

And the test class:

public class TestClass {
    @Test
    public void test1() {
        List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
       
        list.add(new D(1));
        list.add(new D(10));
        list.add(new D(5));
       
        ISort<?> sorter = new DSort();
           
        sorter.sort(list);       
    }
}

The compiler gives an error at the line:

sorter.sort(list);

And states:

The method sort(List<capture#2-of ?>)
in the type ISort<capture#2-of ?>
is not applicable for the arguments
 (List<InternalTreeItem<?>>)

We realized the problem lies with Collections.sort(List<T> list, Comparator<? super T> c) in the abstract class Sort, as I use a Comparator<? extends T>.

I use generics, as I have 2 models, one model's super class is a generic abstract subclassed by 35 classes, and the second model actually has 2 different super classes, which combined, are subclassed by again 35 classes. These hierarchies are given, there's nothing I can do to modify them.

The model here is very simple, but you get the point. Also, there's a factory, that depending on the type of T, returns one sorter, or another.

How can I solve this issue? That is, to sort a generic list; the parameter type can be a generic superclass or one of it's subclasses.

like image 362
Domi Avatar asked Jul 26 '26 00:07

Domi


2 Answers

One way to approach this is to use a wrapper class for the classes that you cannot change.

So in your example you want to order a list of object D, based on an Integer value. By putting your objects in a wrapper and then adding this to the list, you can expose the value you wish to sort the list by.

For example, you could define an interface like:

private interface SortableListItem<T> extends Comparable<SortableListItem<T>> {
    public T getValue();
}

Then, create a wrapper class for D:

public class DWrapper implements SortableListItem<Integer> {
    private D item;

    public DWrapper(D item) {
        this.item = item;
    }

    public Integer getValue() {
        return item.getInteger();
    }

    public int compareTo(SortableListItem<Integer> o) {
        return getValue().compareTo(o.getValue());
    }
}

From here it is pretty simple to create and sort your list:

    D item1= new D(1);
    D item2= new D(10);
    D item3= new D(5);

    DWrapper wrapper1 = new DWrapper(item1);
    DWrapper wrapper2= new DWrapper(item2);
    DWrapper wrapper3= new DWrapper(item3);

    List<SortableListItem<Integer>> sortableList = new  ArrayList<SortableListItem<Integer>>();
    sortableList.add(wrapper1 );
    sortableList.add(wrapper2);
    sortableList.add(wrapper3);
    Collections.sort(sortableList);

You can of course make the wrapper class accept a more generic object - the key is that each object returns a value (in this case an Integer) that the List can be sorted by.

like image 136
RichardP Avatar answered Jul 27 '26 14:07

RichardP


The variable sorter is of type ISort<?>. It could have, say, an ISort<String> assigned to it. The sort method takes an argument of List<T> where T could be String. Clearly you cannot use List<InternalTreeItem<?>> for List<String>, so fortunately the compiler points out the error.

(Note: It's generally a good idea to keep to coding conventions. No I Hungarian prefixes, or single letter class names.)

like image 31
Tom Hawtin - tackline Avatar answered Jul 27 '26 14:07

Tom Hawtin - tackline



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!