Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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

Running your code what I can deduce is that you get a compile error since it is not possible to capture the wildcard that you specify in below line of class TestClass:

ISort<?> sorter = new DSort();

As I understand an occurrence of wild card is taken to stand for some unknown type and from your code it is not possible to infer the type (for the compiler).

But looking at the code, the class DSort is not written in a way to take type parameters
and any attempt to pass type parameters during creation of instance of DSort gave the error:
The type DSort is not generic; it cannot be parameterized with arguments

But you mention that you cannot alter the code of the modules (i.e I presume of classes DSort etc).
So one way to fix the error would be to not use generics during creation of instance of ISort.
The below code works and the prints the the sorted output (1,5,10)

List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));

// no generic arguments
ISort sorter = new DSort();

List<InternalTreeItem<?>> sortedList = sorter.sort(list);

for(InternalTreeItem i:sortedList) {
    System.out.println(i.getValue());
}

but results in a warning of the form ISort is a raw type. References to generic type ISort should be parameterized. But having code that uses generic and having warning of this form is not a good practice . This warning implies that the compiler cannot give cast-iron guarantee about the implicit casts it does to use generics.

If feasible, I think the better solution would be to see how the modules class can re-designed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top