这是如何编译的?
How does this compile?
我正在编写一个函数,该函数采用 keyExtractor 函数列表来生成比较器(假设我们有一个具有许多属性的对象,并且希望能够以任意顺序任意比较大量属性) .
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test {
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) {
if (keyExtractors.isEmpty()) {
return (a, b) -> 0;
} else {
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
}
}
public static void main(String[] args) {
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
}
}
class Extractor<T, S extends Comparable<S>> implements Function<T, S> {
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor) {
this.extractor = extractor;
}
@Override
public S apply(T t) {
return extractor.apply(t);
}
}
class Data {
private final Integer a;
private final Integer b;
private Data(int a, int b) {
this.a = a;
this.b = b;
}
public Integer getA() {
return a;
}
public Integer getB() {
return b;
}
}
我的困惑主要有3点:
1).如果我没有定义 Extractor class,这将无法编译。我不能直接拥有函数或某种功能接口。
2).如果我删除标识函数映射行“.map(e -> e)”,这将不会进行类型检查。
3).我的 IDE 说我的函数正在接受数据类型的函数列表 -> ?这不符合 parseKeysAscending 函数的范围。
它对我来说没有 Extractor
class 也没有在流管道中调用 map(e -> e)
。实际上,如果您使用正确的泛型类型,则根本不需要流式传输提取器列表。
至于为什么你的代码不起作用,我不太确定。泛型是 Java 的一个艰难而不稳定的方面...我所做的只是调整 parseKeysAscending
方法的签名,使其符合 Comparator.comparing
实际期望的内容。
这是 parseKeysAscending
方法:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors) {
if (keyExtractors.isEmpty()) {
return (a, b) -> 0;
} else {
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.<T, S>comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
}
}
下面是调用的演示:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
使代码在没有警告的情况下编译的唯一方法是将函数列表声明为 List<Function<Data, Integer>>
。但这仅适用于 return Integer
的吸气剂。我假设您可能想要比较 Comparable
的任何组合,即上面的代码适用于以下 Data
class:
public class Data {
private final Integer a;
private final String b;
private Data(int a, String b) {
this.a = a;
this.b = b;
}
public Integer getA() {
return a;
}
public String getB() {
return b;
}
@Override
public String toString() {
return "[" + a + ", '" + b + "']";
}
}
这是 demo。
编辑: 请注意,对于 Java 8,parseKeysAscending
方法的最后一行可以是:
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
而对于较新版本的 Java,您必须提供明确的通用类型:
return Comparator.<T, S>comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
在 Federico 纠正我之后(谢谢!)这是您可以使用的单一方法:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list) {
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
}
用法为:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
- If I don't define the
Extractor
class, this will not compile. I cannot directly have Function
s or some sort of functional interface.
不,你可以。您可以通过 lambda 或方法引用或匿名 class.
定义任何 Function<X, Y>
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
还是会的,只是结果不一定适合这个方法。您始终可以明确定义通用参数,以确保一切如您所愿。
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
但这里不需要:
Comparator<Data> test = parseKeysAscending(extractors);
- My IDE says my function is accepting a
List
of Function
s of the type Data, ?
which doesn't comply with the bounds of the parseKeysAscending
function.
是的,应该。您正在传递 List<Extractor<Data, ?>>
并且编译器无法理解 ?
部分。它可能是也可能不是 Comparable
而您的方法显然需要 S extends Comparable<S>
.
关于问题中的原始代码:您可以删除 Extractor
并使用原始 Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S。但是我在这里看不到如何摆脱原始类型...
我正在编写一个函数,该函数采用 keyExtractor 函数列表来生成比较器(假设我们有一个具有许多属性的对象,并且希望能够以任意顺序任意比较大量属性) .
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
class Test {
public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) {
if (keyExtractors.isEmpty()) {
return (a, b) -> 0;
} else {
Function<T, S> firstSortKey = keyExtractors.get(0);
List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
}
}
public static void main(String[] args) {
List<Extractor<Data, ?>> extractors = new ArrayList<>();
extractors.add(new Extractor<>(Data::getA));
extractors.add(new Extractor<>(Data::getB));
Comparator<Data> test = parseKeysAscending(
extractors.stream()
.map(e -> e)
.collect(Collectors.toList()));
}
}
class Extractor<T, S extends Comparable<S>> implements Function<T, S> {
private final Function<T, S> extractor;
Extractor(Function<T, S> extractor) {
this.extractor = extractor;
}
@Override
public S apply(T t) {
return extractor.apply(t);
}
}
class Data {
private final Integer a;
private final Integer b;
private Data(int a, int b) {
this.a = a;
this.b = b;
}
public Integer getA() {
return a;
}
public Integer getB() {
return b;
}
}
我的困惑主要有3点:
1).如果我没有定义 Extractor class,这将无法编译。我不能直接拥有函数或某种功能接口。
2).如果我删除标识函数映射行“.map(e -> e)”,这将不会进行类型检查。
3).我的 IDE 说我的函数正在接受数据类型的函数列表 -> ?这不符合 parseKeysAscending 函数的范围。
它对我来说没有 Extractor
class 也没有在流管道中调用 map(e -> e)
。实际上,如果您使用正确的泛型类型,则根本不需要流式传输提取器列表。
至于为什么你的代码不起作用,我不太确定。泛型是 Java 的一个艰难而不稳定的方面...我所做的只是调整 parseKeysAscending
方法的签名,使其符合 Comparator.comparing
实际期望的内容。
这是 parseKeysAscending
方法:
public static <T, S extends Comparable<? super S>> Comparator<T> parseKeysAscending(
List<Function<? super T, ? extends S>> keyExtractors) {
if (keyExtractors.isEmpty()) {
return (a, b) -> 0;
} else {
Function<? super T, ? extends S> firstSortKey = keyExtractors.get(0);
List<Function<? super T, ? extends S>> restOfSortKeys =
keyExtractors.subList(1, keyExtractors.size());
return Comparator.<T, S>comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
}
}
下面是调用的演示:
List<Function<? super Data, ? extends Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
Comparator<Data> test = parseKeysAscending(extractors);
List<Data> data = new ArrayList<>(Arrays.asList(
new Data(1, "z"),
new Data(2, "b"),
new Data(1, "a")));
System.out.println(data); // [[1, 'z'], [2, 'b'], [1, 'a']]
data.sort(test);
System.out.println(data); // [[1, 'a'], [1, 'z'], [2, 'b']]
使代码在没有警告的情况下编译的唯一方法是将函数列表声明为 List<Function<Data, Integer>>
。但这仅适用于 return Integer
的吸气剂。我假设您可能想要比较 Comparable
的任何组合,即上面的代码适用于以下 Data
class:
public class Data {
private final Integer a;
private final String b;
private Data(int a, String b) {
this.a = a;
this.b = b;
}
public Integer getA() {
return a;
}
public String getB() {
return b;
}
@Override
public String toString() {
return "[" + a + ", '" + b + "']";
}
}
这是 demo。
编辑: 请注意,对于 Java 8,parseKeysAscending
方法的最后一行可以是:
return Comparator.comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
而对于较新版本的 Java,您必须提供明确的通用类型:
return Comparator.<T, S>comparing(firstSortKey)
.thenComparing(parseKeysAscending(restOfSortKeys));
在 Federico 纠正我之后(谢谢!)这是您可以使用的单一方法:
public static <T, S extends Comparable<? super S>> Comparator<T> test(List<Function<T, S>> list) {
return list.stream()
.reduce((x, y) -> 0,
Comparator::thenComparing,
Comparator::thenComparing);
}
用法为:
// I still don't know how to avoid this raw type here
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA); // getA returns an Integer
extractors.add(Data::getB); // getB returns a String
listOfSomeDatas.sort(test(extractors));
- If I don't define the
Extractor
class, this will not compile. I cannot directly haveFunction
s or some sort of functional interface.
不,你可以。您可以通过 lambda 或方法引用或匿名 class.
定义任何Function<X, Y>
List<Function<Data, Integer>> extractors = List.of(Data::getA, Data::getB);
- If I remove the identity function mapping line
.map(e -> e)
, this will not type check.
还是会的,只是结果不一定适合这个方法。您始终可以明确定义通用参数,以确保一切如您所愿。
extractors.<Function<Data, Integer>>stream().collect(Collectors.toList())
但这里不需要:
Comparator<Data> test = parseKeysAscending(extractors);
- My IDE says my function is accepting a
List
ofFunction
s of the typeData, ?
which doesn't comply with the bounds of theparseKeysAscending
function.
是的,应该。您正在传递 List<Extractor<Data, ?>>
并且编译器无法理解 ?
部分。它可能是也可能不是 Comparable
而您的方法显然需要 S extends Comparable<S>
.
关于问题中的原始代码:您可以删除 Extractor
并使用原始 Comparable
:
List<Function<Data, Comparable>> extractors = new ArrayList<>();
extractors.add(Data::getA);
extractors.add(Data::getB);
@SuppressWarnings("unchecked")
Comparator<Data> test = parseKeysAscending(extractors);
P.S。但是我在这里看不到如何摆脱原始类型...