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

如何在Java8中为void(不是void)方法指定函数类型?,java,java-8,Java,Java 8,我正在使用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(functionToB

我正在使用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);
    }
}

编译器抱怨说,
void无法转换为void
。我不知道如何在
myForEach
的签名中指定函数接口的类型,以便代码编译。我知道我可以简单地将
displayInt
的返回类型更改为
Void
,然后返回
null
。但是,在某些情况下,可能无法更改我希望传递给其他地方的方法。有没有一种简单的方法可以按原样重用
displayInt

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

函数类型声明为

interface Function<T,R> {
  R apply(T t);
}
或者我也可以这么做

allJedi.forEach(System.out::println);
println
方法是合适的,因为它接收值并具有返回类型void,就像Consumer中的
accept
方法一样

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

public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
   list.forEach(myBlock);
}
最终,您甚至可以完全摆脱
myForEach
方法,只需执行以下操作:

theList.forEach(Test::displayInt);
关于一等公民的职能


尽管如此,事实是Java8将不会具有作为一级公民的函数,因为不会向语言中添加结构函数类型。Java将提供一种替代方法,用lambda表达式和方法引用创建函数接口的实现。最终,lambda表达式和方法引用将绑定到对象引用,因此我们所拥有的只是作为一等公民的对象。重要的是功能是存在的,因为我们可以将对象作为参数传递,将它们绑定到变量引用,并从其他方法将它们作为值返回,那么它们几乎可以起到类似的作用。

当您需要接受一个函数作为参数,它不接受任何参数,也不返回任何结果(void),在我看来,最好还是有这样的东西

  public interface Thunk { void apply(); }
在代码的某个地方。在我的函数编程课程中,“thunk”一词被用来描述这些函数。我无法理解为什么它不在java.util.function中


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

我觉得您应该使用消费者界面,而不是
函数

使用者基本上是一个功能接口,设计用于接受值而不返回任何内容(即void)

在您的情况下,您可以在代码的其他地方创建一个使用者,如下所示:

Consumer<String> block = System.out::println
Consumer<Integer> myFunction = x -> {
    System.out.println("processing value: " + x);    
    .... do some more things with "x" which returns nothing...
}

您将myFunction视为一级对象。

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

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


顺便说一句,
Block
在JDK 8 API的最新版本中被更改为
Consumer
。最后一段掩盖了一些重要事实:Lambda表达式和方法引用不仅仅是一个语法添加。JVM本身提供了比匿名类更有效地处理方法引用的新类型(最重要的是
MethodHandle
),并且使用这一点,Java编译器能够生成更有效的代码,例如,在不使用闭包的情况下实现lambda作为静态方法,因此防止了额外类的生成。而
函数的正确版本是
可运行的
@OrangeDog这并不完全正确。在
Runnable
Callable
接口的注释中,写着它们应该与一起使用。这样做的恼人后果是,如果直接调用
run()
方法,一些静态分析工具(如Sonar)会抱怨,我想知道Java在什么情况下不能将函数作为一级公民对待,即使在自Java8引入函数接口和lambda之后也是如此?特别是
Runnable
不允许检查异常,这使得它对许多应用程序毫无用处。由于
Callable
也没有用处,因此您需要定义自己的代码。关于检查过的异常问题,Java的新函数通常很难解决这个问题。对于流API之类的东西来说,它是一个巨大的拦截器。他们的设计非常糟糕。除了现有的答案之外,这又增加了什么?
theList.forEach(Test::displayInt);
  public interface Thunk { void apply(); }
Consumer<Integer> myFunction = x -> {
    System.out.println("processing value: " + x);    
    .... do some more things with "x" which returns nothing...
}
public static void myForEach(List<Integer> list, Consumer<Integer> myFunction) 
{
  list.forEach(x->myFunction.accept(x));
}
// 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;});