如何在Java8中为void(不是void)方法指定函数类型?
我正在使用Java8来了解作为一等公民的功能。我有以下片段:如何在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
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;});