返回具有受泛型类型参数约束的参数类型的lambda函数

时间:2017-01-12 22:46:51

标签: java generics lambda types type-inference

假设我想编写一个方法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提出的解决方案都有效。一旦我做了更好的工作来提炼原始问题,我会发布一个新问题。

2 个答案:

答案 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);时,编译器无论如何都会推断ObjectT。这允许传递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");