我有以下课程:
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<?>>)
我们意识到问题出在抽象类
Collections.sort(List<T> list, Comparator<? super T> c)
中的Sort
,因为我使用了Comparator<? extends T>
。
我使用泛型,因为我有 2 个模型,一个模型的超类是由 35 个类划分的通用抽象子类,第二个模型实际上有 2 个不同的超类,它们组合起来又由 35 个类划分子类。这些层次结构是给定的,我无法修改它们。
这里的模型非常简单,但你明白了。此外,还有一个工厂,根据
T
的类型,返回一个或另一个排序器。
如何解决这个问题?也就是说,对通用列表进行排序;参数类型可以是通用超类或其子类之一。
解决此问题的一种方法是对无法更改的类使用包装类。
因此,在您的示例中,您想要基于整数值对对象 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);
您当然可以使包装类接受更通用的对象 - 关键是每个对象返回一个值(在本例中是一个整数),列表可以根据该值进行排序。
变量
sorter
的类型为 ISort<?>
。例如,它可以分配一个 ISort<String>
。 sort
方法采用 List<T>
参数,其中 T
可以是 String
。显然你不能使用 List<InternalTreeItem<?>>
来代替 List<String>
,所以幸运的是编译器指出了这个错误。
(注意:遵守编码约定通常是个好主意。没有
I
匈牙利前缀或单字母类名称。)
运行您的代码,我可以推断出您会收到编译错误,因为无法捕获您在类TestClass下面的行中指定的通配符:
ISort<?> sorter = new DSort();
ISort<?> sorter = new DSort();
据我了解,通配符的出现代表某种未知类型,并且从您的代码中不可能推断出该类型(对于编译器)。
但是看代码,DSort类并不是以接受类型参数的方式编写的
并且在创建 DSort 实例期间尝试传递类型参数会出现错误:
但是你提到你不能改变模块的代码(即我假设类 DSort 等)。
因此,修复该错误的一种方法是在创建 ISort 实例期间不使用泛型。
下面的代码有效并打印排序后的输出(1,5,10)
The type DSort is not generic; it cannot be parameterized with arguments
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 is a raw type。对通用类型 ISort 的引用应该参数化。但是拥有使用泛型的代码并发出这种形式的警告并不是一个好的做法。此警告意味着编译器无法对使用泛型进行的隐式转换提供严格的保证。
如果可行,我认为更好的解决方案是看看如何重新设计模块类。