如何在 Java8中为 Void (而不是 Void)方法指定函数类型?

我正在使用 Java8来了解作为一等公民的函数是如何运行的:

package test;


import java.util.*;
import java.util.function.*;


public class Test {


public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
list.forEach(functionToBlock(myFunction));
}


public static void displayInt(Integer i) {
System.out.println(i);
}




public static void main(String[] args) {
List<Integer> theList = new ArrayList<>();
theList.add(1);
theList.add(2);
theList.add(3);
theList.add(4);
theList.add(5);
theList.add(6);
myForEach(theList, Test::displayInt);
}
}

我要做的是使用方法引用将方法 displayInt传递给方法 myForEach。编译器将产生以下错误:

src/test/Test.java:9: error: cannot find symbol
list.forEach(functionToBlock(myFunction));
^
symbol:   method functionToBlock(Function<Integer,Void>)
location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
myForEach(theList, Test::displayInt);
^
required: List<Integer>,Function<Integer,Void>
found: List<Integer>,Test::displayInt
reason: argument mismatch; bad return type in method reference
void cannot be converted to Void

编译器抱怨 void cannot be converted to Void。我不知道如何在 myForEach的签名中指定函数接口的类型,以便编译代码。我知道我可以简单地将 displayInt的返回类型改为 Void,然后返回 null。但是,在某些情况下,可能无法更改要在其他地方传递的方法。是否有一种简单的方法来重用 displayInt

170913 次浏览

您试图使用错误的接口类型。在这种情况下,类型 功能不合适,因为它接收一个参数并具有返回值。相反,您应该使用 消费者(以前称为 Block)

函数类型声明为

interface Function<T,R> {
R apply(T t);
}

但是,Consumer 类型与您正在查找的类型兼容:

interface Consumer<T> {
void accept(T t);
}

因此,Consumer 与接收 T 并且不返回任何值(void)的方法兼容。这就是你想要的。

例如,如果我想显示一个列表中的所有元素,我可以简单地用 lambda 表达式为它创建一个使用者:

List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );

在上面的例子中可以看到,lambda 表达式接收一个参数,没有返回值。

现在,如果我想使用方法引用而不是 lambda 表达式来创建这种类型的使用,那么我需要一个接收 String 并返回 void 的方法,对吗?.

我可以使用不同类型的方法引用,但在本例中,让我们通过在 System.out对象中使用 println方法来利用对象方法引用,如下所示:

Consumer<String> block = System.out::println

或者我也可以

allJedi.forEach(System.out::println);

println方法是合适的,因为它接收到一个值并具有一个返回类型 void,就像 Consumer 中的 accept方法一样。

因此,在您的代码中,您需要将您的方法签名更改为类似于:

public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
list.forEach(myBlock);
}

然后,您应该能够使用静态方法引用创建一个使用者,在您的例子中,方法是:

myForEach(theList, Test::displayInt);

最终,你甚至可以完全摆脱 myForEach方法,只需要这样做:

theList.forEach(Test::displayInt);

论一级公民的职能

总而言之,事实是 Java8将不会有作为一级公民的函数,因为结构化函数类型将不会被添加到语言中。Java 只是提供了一种替代方法,用 lambda 表达式和方法引用创建函数接口的实现。最终,lambda 表达式和方法引用将绑定到对象引用,因此我们所有的对象都是一等公民。重要的是它的功能性,因为我们可以将对象作为参数传递,将它们绑定到变量引用,然后将它们作为其他方法的值返回,这样它们就可以起到类似的作用。

当您需要接受一个不接受任何参数且不返回任何 result (void)的函数作为参数时,在我看来,最好还是使用

  public interface Thunk { void apply(); }

在你的代码中的某个地方。在我的函数式编程课程中,“ thunk”一词被用来描述这样的函数。为什么它不在 java.util.function 中,这超出了我的理解范围。

在其他情况下,我发现即使 java.util.function 确实有一些与我想要的签名相匹配的东西——当接口的命名与我的代码中函数的使用不匹配时,它仍然不总是感觉正确的。我想这里提到的关于“ Runnable”(一个与 Thread 类相关的术语)的观点与此类似,所以尽管它可能有我需要的签名,但它仍然可能会让读者感到困惑。

将返回类型设置为 Void而不是 voidreturn null

// Modify existing method
public static Void displayInt(Integer i) {
System.out.println(i);
return null;
}

或者

// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});