public Array<ClassB> sort(Array<Object> input, Attribute x){
这会将任意对象数组转换为包含ClassB 实例的数组,这显然不是它的工作原理。
Array<Object> input
这并不意味着你认为它意味着什么。 Array<ClassB> 不是Array<Object> 的有效值。毕竟,我可以将字符串添加到Array<Object>(字符串是对象!),但我不能将字符串添加到Array<ClassB>。在计算机技术术语中,Java 中的基本类型(如 Number 和 Integer)是协变的 - 任何作为 X 子类型的实例也是 X:整数也是数字,是也是一个对象。但是类型 args 是不变的。任何属于 X 子类型的实例都与 X 不兼容:List<Integer> 不是List<Number>。这是因为这就是它的工作原理,正如我在上面展示的:您可以将双打添加到 List<Number>。
您可以选择不同的差异:List<? extends Number> 或 List<? super Number> 这样做。不过,您不需要它来解决这个问题。
泛型链接事物。这就是你想要的:接受一些东西的数组,然后返回一个相同的数组:
public <T> Array<T> sort(Array<T> input, Attribute x) {}
这说:有一些类型T。不知道它是什么。然后这个 T 用于链接事物:无论输入数组的类型是什么?输出数组具有相同的类型。
这是你想要的。
Attribute x
无法以编程方式引用属性。幸运的是,您实际上并不需要属性。你需要一个函数:给定一些T,提供一个值;一个,你现在如何固有地排序。这个功能是如何工作的?你不在乎。只要它是一致的(为任何给定实例返回相同的值),您就可以根据该映射值对其进行排序。所以,你不想要Attribute,你想要一个把T变成你可以排序的东西的函数。您有三个选择:
继续狂欢,列出你拥有的每一个可排序的概念。
public <T> Array<T> sortByInt(Array<T> input, ToIntFunction<T> f) {}
public <T> Array<T> sortByString(Array<T> input, Function<T, String> f) {}
public <T> Array<T> sortByDouble(Array<T> input, ToDoubleFunction<T, String> f) {}
等等。这些方法确实需要不同的名称,这里不要重载。
映射到反对和祈祷
您不知道如何对任何给定对象进行排序。您大概只知道如何对整数、双精度和字符串进行排序。你可以写:
public <T> Array<T> sortByDouble(Array<T> input, Function<T, ?> f) {}
然后在运行时检查函数是否映射到您想要的。如果函数映射到,比如说,InputStream,这显然不是你可以排序的东西,你必须检查它,然后抛出一个异常。如果一个对象映射到一个 int 而另一个对象映射到一个字符串,它也会变得相当混乱。这听起来很简单,但事实并非如此。不要这样做。
使用 java' 内置的自订概念。
Java 对此有一个类型:Comparable。这有点复杂。在这里,您希望对象可以与自己进行比较,所以:
public <T, V extends Comparable<V>> Array<T> sort(Array<T> input, Function<T, V> f) {}
这有点复杂,但它是最正确的版本。让我们来看看吧:
-
<T, V... > 部分:这表示此方法有 2 个类型变量。它们与普通变量一样,只是它们包含类型。
-
T:它没有任何界限。因此,它可以是任何东西。任何调用此方法的代码都将显式选择某些内容,或者编译器会找出答案。
-
V:这是有界限的。 V不能只是任何东西。 V 必须是某种类型,它具有它实现的属性Comparable<V> - 换句话说,一种可以将自己与自己类型的另一个值进行比较的东西。 String,Integer,还有很多其他java内置的东西都是这样的。 String 是 V 填写的有效类型。
-
Array<T> - 方法返回的内容。
-
sort - 方法名
-
Array<T> input - 输入。请注意,这与返回类型相关联。
-
Function<T, V> - 一个函数。这个函数将接收一个类型为 T 的对象并返回一个类型为 V 的对象。记住,V 不能只是任何东西——它需要是某种特定的类型,可以对自身与其他相同类型的东西进行排序,例如String 或 Integer。
有了这些,你就可以编写一些代码了。例如:
T first = input.get(0); // still no idea what T is. But `input.get(0)` returns it.
T second = input.get(1);
V firstAttribute = function.apply(first); // extracts the sortable attribute
V secondAttribute = function.apply(second);
int ordering = firstAttribute.compareTo(secondAttribute); // magic!
最后一行是一个奇迹。它可以编译并保证可以工作:firstAttribute 是V,虽然我们不知道 V 是什么(可能是字符串。也可能是整数),但我们知道 V 有 int compareTo(V other) 方法。所以,我们可以调用它。负数表示first 在second 之前,正数表示first 在第二个之后,0 响应表示first 和second 要么相等,要么就比较它们而言,兄弟姐妹 - 两者都不是“之前”。
这个函数就是编写排序算法所需要的。
请注意,鉴于first 是T,您可以这样写:
Array<T> output = new Array<T>();
output.add(first);
编译器不知道T是什么,但它知道first是一个T,所以这个调用是允许的。