Java类层次结构与泛型、比较器和排序错误

3

我一直在寻找能帮助我解决问题的东西,但到目前为止还没有找到。我有以下的类:

  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());
    }
}

测试类如下:

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);       
    }
}

编译器在该行报错。
sorter.sort(list);

以及州份

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

好的,在朋友的帮助下,经过几个小时的研究,我们发现问题出在抽象类Sort中的Collections#sort(List<T> list, Comparator<? super T> c)方法上,因为我使用了一个Comparator<? extends T>

我使用泛型,因为我有两个模型,一个模型的超类是一个通用的抽象子类,由35个类继承,而第二个模型实际上有两个不同的超类,这两个超类组合起来又被35个类继承。这些层次结构是固定的,我无法修改它们。

这里的模型非常简单,但你明白我的意思。此外,还有一个工厂,根据T的类型返回一个排序器或另一个排序器。

请问有人能帮忙提供解决方案吗(即对一个通用列表进行排序;参数类型可以是通用超类或其子类之一)。

谢谢和最好的祝福, Domi

3个回答

2

一种处理方法是使用一个包装类来包装那些你无法更改的类。

所以在你的例子中,你想要根据一个整数值对对象D的列表进行排序。通过将你的对象放入一个包装器中,然后将其添加到列表中,你可以公开你希望按照其进行排序的值。

例如,你可以定义一个接口,如下:

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

接下来,为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());
    }
}

从这里开始,创建和排序列表非常简单:

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

当然,你可以让包装类接受一个更通用的对象 - 关键是每个对象返回一个值(在这种情况下是整数),以便列表可以按照该值进行排序。

你好RichardP。我非常喜欢你的解决方案,只是我有两个模型需要应用它,这超过了70个类 :| - Domi
2
70个类,听起来确实是很多工作。如果您编写包装器类,使它们接受超类的实例而不是为每个子类编写一个包装器,那么您需要做的工作就会少得多。 - RichardP

1
变量sorter的类型为ISort<?>。它可以被赋值为ISort<String>。方法sort需要一个参数List<T>,其中T可以是String。显然,你不能使用List<InternalTreeItem<?>>代替List<String>,所以幸运的是编译器指出了错误。
(注意:遵循编码规范通常是个好主意。不要使用I匈牙利前缀或单字母类名。)

嗨Tom,感谢你的评论 :) 但是我注意到编译器指出了不同的错误。正如我之前所述,sort方法需要将Comparator类型参数化为? super T,而我传递给它的类型实际上是? extends T - Domi

0
从你的代码运行结果来看,我可以推断出你遇到了编译错误,因为在TestClass类下面这行代码中指定的通配符是无法捕获的:
ISort<?> sorter = new DSort();

据我理解,通配符的出现被视为某种未知类型,从您的代码中无法推断出类型(对于编译器而言)。

但是看着代码,DSort类并没有以接受类型参数的方式编写,
任何在创建DSort实例时尝试传递类型参数的尝试都会导致错误:
The type DSort is not generic; it cannot be parameterized with arguments

但是您提到您不能更改模块的代码(即我假设的DSort类等)。
因此,修复错误的一种方法是在创建ISort实例时不使用泛型。
以下代码可以正常工作并打印排序后的输出(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());
}

但结果会出现一个警告的形式 ISort是一个原始类型。对泛型类型ISort的引用应该被参数化。但是代码使用了泛型并且有这种形式的警告不是一种好的做法。这个警告意味着编译器无法对隐式转换使用泛型给予铁打保证。
如果可行的话,我认为更好的解决方案是看看模块类如何重新设计。

嗨,Sateesh,不幸的是,我无法修改模型类层次结构,而我所指的模型类层次结构是指其中一个具有由35个类子类化的通用类,另一个具有两个顶级类。这些层次结构不能以任何方式更改。 - Domi
也许你需要使用原始类型(不带泛型)并接受警告。 - sateesh
我不认为那会是一个选择 :( - Domi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接