假设我想编写一个方法buildBiFxnWithSameTypeArgs
,它生成一个双参数BiFunction
,其输入参数的类型相同。事先不知道(即调用buildBiFxnWithSameTypeArgs
时)具体是什么类型 - 我只是希望编译器在调用buildBiFxnWithSameTypeArgs
返回的函数时强制执行该类型提供给它的参数必须匹配。最终效果应该是lambda等效于在方法定义中对两个参数的类型使用相同的泛型类型参数。
我的初步尝试如下:
public interface ConstrainedBiFunction<I, O> extends BiFunction<I, I, O> {}
public static ConstrainedBiFunction<?, String> buildBiFxnWithSameTypeArgs() {
return (inputOne, inputTwo) -> String.valueOf(inputOne) + String.valueOf(inputTwo);
}
public void test() {
buildBiFxnWithSameTypeArgs().apply(Integer.valueOf(1), Integer.valueOf(2));
}
然而,似乎不能以这种方式使用通配符类型参数?
; apply
步骤上的编译失败,出现以下错误:
apply (capture<?>, capture<?> in BiFunction cannot be applied to (java.lang.Integer, java.lang.Integer)
是否可以返回推断出参数类型的lambda函数,然后以这种方式相对于彼此进行约束?
编辑:为不太好的问题道歉;我有一个更复杂的问题,我试图捕捉本质,以便在这里提出最简单的问题,但看起来我过分简化了问题。当然,我知道所有类都继承自Object
,所以在这个过度简化的问题再现中,@ shmosel和@JB Nizet提出的解决方案都有效。一旦我做了更好的工作来提炼原始问题,我会发布一个新问题。
答案 0 :(得分:1)
通配符表示“特定但未知类型”。您不能传递任何参数,该参数将满足?
类型的参数,因为它可能是错误的类型。将返回类型更改为ConstrainedBiFunction<Object, String>
,它将能够接受任何输入类型,因为每个类都隐式扩展Object
:
public static ConstrainedBiFunction<Object, String> buildBiFxnWithSameTypeArgs() {
return (inputOne, inputTwo) -> String.valueOf(inputOne) + String.valueOf(inputTwo);
}
请注意,使用PECS原则,这仍然可以用于对输入类型有限制的方法。例如:
// input parameter must be of type Integer or any supertype,
// so that we can safely pass in an Integer
String combine(ConstrainedBiFunction<? super Integer, String> function) {
return function.apply(1, 2);
}
void test() {
ConstrainedBiFunction<Object, String> concat = buildBiFxnWithSameTypeArgs();
ConstrainedBiFunction<Integer, String> sum = (a, b) -> String.valueOf(a + b);
System.out.println(combine(concat)); // "12"
System.out.println(combine(sum)); // "3"
}
答案 1 :(得分:0)
虽然shmosel’s answer正确指出使用Object
作为输入类型应该足以满足遵循PECS规则的所有API,但可以修复原始尝试。
您必须添加一个允许调用者选择类型的类型参数,而不是通配符:
public interface ConstrainedBiFunction<I, O> extends BiFunction<I, I, O> {}
public static <T> ConstrainedBiFunction<T, String> buildBiFxnWithSameTypeArgs() {
return (inputOne, inputTwo) -> String.valueOf(inputOne) + String.valueOf(inputTwo);
}
public void test() {
ConstrainedBiFunction<Integer, String> f1 = buildBiFxnWithSameTypeArgs();
String s1 = f1.apply(1, 1);
ConstrainedBiFunction<String, String> f2 = buildBiFxnWithSameTypeArgs();
String s2 = f2.apply("hello ", "world");
}
请注意,将其用作buildBiFxnWithSameTypeArgs().apply(1, 1);
时,编译器无论如何都会推断Object
为T
。这允许传递Integer
个参数,当然,还有其他任何东西,这使得两个参数具有相同类型的想法没有实际意义,即buildBiFxnWithSameTypeArgs().apply(1.5, "foo");
也适用,因为两个参数都可以赋值给相同的类型Object
。
这些结构有一些现实生活中的例子来处理无法通过PECS解决的问题。
E.g。有Function.identity()
返回具有相同输入和输出类型的任意类型函数。将此函数传递给toMap
collector时,您可以获得与流元素具有相同键或值类型的映射。
更复杂的例子是Comparator.naturalOrder()
。这里,type参数有一个绑定,确保要比较的元素确实是Comparable
。由于类型不实现Comparable<?>
,而是实现自身的类型,naturalOrder()
在没有类型参数的情况下不起作用。
所以你不能做Comparator.naturalOrder().compare(42, "foo")
。但你可以做到
Comparator<Integer> c1 = Comparator.naturalOrder();
c1.compare(10, 42);
Comparator<String> c2 = Comparator.naturalOrder();
c2.compare("foo", "bar");