Java lambda 可以将方法绑定到它们的参数吗?
Can Java lambdas bind methods to their parameters?
这里讨论如何使用 lambda 将方法作为参数传递:
Java Pass Method as Parameter
在其他语言中,即 C++,可以使用 Lambdas 将函数绑定到它的参数 - 此处讨论:
Bind Vs Lambda?
是否可以在 Java 中使用 lambda 绑定方法?
如果是这样,您将如何完成?
编辑>>>>
根据要求,我通常尝试做的一个例子:
注意,这里有伪代码。
public class DataView {
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
//Pseudo Code:
boundFunction mybind = boundFunction(functionA, 5, 10);
boundFunction mybind2 = boundFunction(functionB, 10, 12);
iter(mybind);
iter(mybind2);
}
//Method with pseudo parameter
private void iter(functionSignature){
for(Float i : textData){
//Pseudo call to pseudo parameter
functionSignature();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
记住,我不是在寻找 'another way to accomplish this functionality' - 这个例子是为了说明我想使用函数作为参数并将参数绑定到这些函数的一般方法。
编辑>>>
尝试使用匿名 类:
public class DataView {
private class Bound{
public void run(){}
}
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
Bound mybind = new Bound(){
public void run(){
functionA(5,10);
}
};
Bound mybind2 = new Bound(){
public void run(){
functionB(5,10);
}
};
iter(mybind);
iter(mybind2);
}
private void iter(Bound function){
for(Float i : textData){
function.run();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
查看第二个背后的代码link:
auto dice = [&]() { return distribution(engine); };
Java 等价物是:
Supplier<...> dice = () -> distribution.apply(engine);
其中:
distribution
是一个 java.util.function.Function
.
...
是调用的 return 类型。
对于输入和输出类型的不同组合,java.util.function
中提供了不同的功能接口。您还可以定义自己的功能接口。
lambda 可以分配给具有功能接口类型的变量,只要它与接口方法定义的输入和输出类型相匹配。
除了缺少运算符重载和 auto
等价物外,在 Java 中。捕获变量的类型都是引用类型(所有对象)或原始类型。
您可以通过调用(非const
、there is no such thing in Java)方法来更改捕获的引用类型变量。 Like apply
在示例中对 distribution
调用。
所有捕获的变量必须是 final
,或者实际上是最终的。这意味着您不能为捕获的变量分配新值。 IE。 distribution = someVal
,对于引用类型和原始类型都是不允许的。
在您的示例中,您可以这样做:
public class DataView {
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
Runnable mybind = () -> functionA(5, 10);
Runnable mybind2 = () -> functionB(10, 12);
iter(mybind);
iter(mybind2);
}
//Method with pseudo parameter
private void iter(Runnable r){
for(Float i : textData){
r.run();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
我使用 Runnable
因为它是现成的开箱即用的东西。但是如果你有某种复杂的签名,你可以很容易地声明一个功能接口:也就是说,一个只有一个方法的接口(如果你调用 method我们将使用 lambdas)。然后 iter
将接受该接口类型的参数,并且 lambda 表达式通常看起来像 (par1, par2) -> callYourFunction(par1, someValue, par2, someOtherValue)
。这些“值”有点“绑定”,也就是说,就像您在 C++ 中对 [=]
所做的那样进行复制。但这只是就局部变量而言(正如@Jorn 提到的那样,它们必须是有效的最终变量)。但是您可以绝对自由地访问封闭 class 的 字段 (这意味着您甚至可以更改它们)。这在技术上是因为 this
是按值捕获的,就像局部变量一样,因此您可以修改 this
指向的对象。
如其他答案所述,Java 需要实际的功能接口类型来表示功能。这也适用于甚至需要两个这样的接口的绑定操作,一个表示未绑定函数,一个表示绑定函数:
public class DataView {
interface NoArgFunction {
void func();
}
interface TwoIntFunction {
void func(int a, int b);
}
static NoArgFunction bind(TwoIntFunction f, int first, int second) {
return () -> f.func(first, second);
}
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
NoArgFunction mybind = bind(this::functionA, 5, 10);
NoArgFunction mybind2 = bind(this::functionB, 10, 12);
iter(mybind);
iter(mybind2);
}
private void iter(NoArgFunction noArg){
for(Float i : textData){
noArg.func();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
有趣的是,在字节码级别,有一种方法可以在没有中间步骤的情况下将方法与参数结合起来,这正是 lambda 表达式 () -> f.func(first, second)
所使用的,它会被编译进入一个包含 lambda 主体并具有 first
和 second
两个参数的合成方法,这两个参数将在运行时用于构造绑定 first
和 first
的当前值的 NoArgFunction
实例second
.
但是你不能在 Java 源代码中使用它。现有方法唯一支持的绑定是在方法引用 this::functionA
和 this::functionB
中看到的绑定,它们都将当前 this
实例绑定到方法 functionA
和 functionB
.好吧,使用像
这样的 lambda 表达式
NoArgFunction mybind = () -> functionA(5, 10);
首先没有 bind
方法 确实 缩短了过程…
所以最重要的是,试图与试图为不受内在支持的功能建模的语言作斗争没有多大意义。使用 lambda 表达式而不是绑定,以及为此目的预定义的目标类型使代码更简单:
public class DataView {
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
iter(x -> functionA(5, 10));
iter(x -> functionB(10, 12));
}
private void iter(Consumer<Float> function){
textData.forEach(function);
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
这里讨论如何使用 lambda 将方法作为参数传递:
Java Pass Method as Parameter
在其他语言中,即 C++,可以使用 Lambdas 将函数绑定到它的参数 - 此处讨论:
Bind Vs Lambda?
是否可以在 Java 中使用 lambda 绑定方法?
如果是这样,您将如何完成?
编辑>>>>
根据要求,我通常尝试做的一个例子:
注意,这里有伪代码。
public class DataView {
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
//Pseudo Code:
boundFunction mybind = boundFunction(functionA, 5, 10);
boundFunction mybind2 = boundFunction(functionB, 10, 12);
iter(mybind);
iter(mybind2);
}
//Method with pseudo parameter
private void iter(functionSignature){
for(Float i : textData){
//Pseudo call to pseudo parameter
functionSignature();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
记住,我不是在寻找 'another way to accomplish this functionality' - 这个例子是为了说明我想使用函数作为参数并将参数绑定到这些函数的一般方法。
编辑>>>
尝试使用匿名 类:
public class DataView {
private class Bound{
public void run(){}
}
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
Bound mybind = new Bound(){
public void run(){
functionA(5,10);
}
};
Bound mybind2 = new Bound(){
public void run(){
functionB(5,10);
}
};
iter(mybind);
iter(mybind2);
}
private void iter(Bound function){
for(Float i : textData){
function.run();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
查看第二个背后的代码link:
auto dice = [&]() { return distribution(engine); };
Java 等价物是:
Supplier<...> dice = () -> distribution.apply(engine);
其中:
distribution
是一个java.util.function.Function
....
是调用的 return 类型。
对于输入和输出类型的不同组合,java.util.function
中提供了不同的功能接口。您还可以定义自己的功能接口。
lambda 可以分配给具有功能接口类型的变量,只要它与接口方法定义的输入和输出类型相匹配。
除了缺少运算符重载和 auto
等价物外,在 Java 中。捕获变量的类型都是引用类型(所有对象)或原始类型。
您可以通过调用(非const
、there is no such thing in Java)方法来更改捕获的引用类型变量。 Like apply
在示例中对 distribution
调用。
所有捕获的变量必须是 final
,或者实际上是最终的。这意味着您不能为捕获的变量分配新值。 IE。 distribution = someVal
,对于引用类型和原始类型都是不允许的。
在您的示例中,您可以这样做:
public class DataView {
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
Runnable mybind = () -> functionA(5, 10);
Runnable mybind2 = () -> functionB(10, 12);
iter(mybind);
iter(mybind2);
}
//Method with pseudo parameter
private void iter(Runnable r){
for(Float i : textData){
r.run();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
我使用 Runnable
因为它是现成的开箱即用的东西。但是如果你有某种复杂的签名,你可以很容易地声明一个功能接口:也就是说,一个只有一个方法的接口(如果你调用 method我们将使用 lambdas)。然后 iter
将接受该接口类型的参数,并且 lambda 表达式通常看起来像 (par1, par2) -> callYourFunction(par1, someValue, par2, someOtherValue)
。这些“值”有点“绑定”,也就是说,就像您在 C++ 中对 [=]
所做的那样进行复制。但这只是就局部变量而言(正如@Jorn 提到的那样,它们必须是有效的最终变量)。但是您可以绝对自由地访问封闭 class 的 字段 (这意味着您甚至可以更改它们)。这在技术上是因为 this
是按值捕获的,就像局部变量一样,因此您可以修改 this
指向的对象。
如其他答案所述,Java 需要实际的功能接口类型来表示功能。这也适用于甚至需要两个这样的接口的绑定操作,一个表示未绑定函数,一个表示绑定函数:
public class DataView {
interface NoArgFunction {
void func();
}
interface TwoIntFunction {
void func(int a, int b);
}
static NoArgFunction bind(TwoIntFunction f, int first, int second) {
return () -> f.func(first, second);
}
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
NoArgFunction mybind = bind(this::functionA, 5, 10);
NoArgFunction mybind2 = bind(this::functionB, 10, 12);
iter(mybind);
iter(mybind2);
}
private void iter(NoArgFunction noArg){
for(Float i : textData){
noArg.func();
}
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}
有趣的是,在字节码级别,有一种方法可以在没有中间步骤的情况下将方法与参数结合起来,这正是 lambda 表达式 () -> f.func(first, second)
所使用的,它会被编译进入一个包含 lambda 主体并具有 first
和 second
两个参数的合成方法,这两个参数将在运行时用于构造绑定 first
和 first
的当前值的 NoArgFunction
实例second
.
但是你不能在 Java 源代码中使用它。现有方法唯一支持的绑定是在方法引用 this::functionA
和 this::functionB
中看到的绑定,它们都将当前 this
实例绑定到方法 functionA
和 functionB
.好吧,使用像
这样的 lambda 表达式
NoArgFunction mybind = () -> functionA(5, 10);
首先没有 bind
方法 确实 缩短了过程…
所以最重要的是,试图与试图为不受内在支持的功能建模的语言作斗争没有多大意义。使用 lambda 表达式而不是绑定,以及为此目的预定义的目标类型使代码更简单:
public class DataView {
private static ArrayList<Float> rectData = new ArrayList<Float>();
private static ArrayList<Float> textData = new ArrayList<Float>();
DataView(){
iter(x -> functionA(5, 10));
iter(x -> functionB(10, 12));
}
private void iter(Consumer<Float> function){
textData.forEach(function);
}
private void functionA(int a, int b){
//dostuff
}
private void functionB(int a, int b){
//do other stuff
}
}