views:

422

answers:

3

Hello all,

I've been looking around to see if I find something to help me with my problem, but no luck until now. I've got the following classese:

  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<?>>)

Ok, after a couple of hours and help from a friend, 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.

Can any one please help and provide a solution for my issue (that is to sort a generic list; the parameter type can be a generic superclass or one of it's subclasses).

Thanks and best regards, Domi

+1  A: 

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.)

Tom Hawtin - tackline
Hey Tom, thanks for the comment:)But as I noticed, the compiler points out a different error. As I stated before, the sort method required Comparator to be type parameterized with `? super T`, while I pass to it a type that's actually `? extends T`.
Domi
A: 
sateesh
Hey sateesh, unfortunately, I cannot modify the model class hierarchies, and by model class hierarchies I refer to the 2 models, where one has a generic class subclassed by 35 classes, and the other one has two top classes.These hierarchies cannot be changed in any way.
Domi
Maybe then you might have to use raw types (without generics) and live with the warnings
sateesh
I don't think that'll be an option :(
Domi
+2  A: 

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.

RichardP
Hello RichardP. I like your solution a lot, except that I have two models I should apply this to, that is more than 70 classes :|
Domi
70 classes, that does sound like a lot of work. If you can write your wrapper classes so that they take instances of the super class instead of writing one wrapper per sub-class, you'd have a lot less work to do.
RichardP