方法参考 - 将函数传递给带有消费者参数的方法
Method Reference - passing Function to method with Consumer argument
我正在学习 Java 8 中的方法参考,但我很难理解它为什么有效?
class Holder {
private String holded;
public Holder(String holded) {
this.holded = holded;
}
public String getHolded() {
return holded;
}
}
private void run() {
Function<Holder, String> getHolded = Holder::getHolded;
consume(Holder::getHolded); //This is correct...
consume(getHolded); //...but this is not
}
private void consume(Consumer<Holder> consumer) {
consumer.accept(null);
}
正如您在 run
方法中看到的那样 - Holder::getHolded
returns 未绑定方法引用,您可以通过将类型 Holder
的对象作为参数传递来调用它。像这样:getHolded.apply(holder)
但是为什么它在直接作为方法参数调用时将这个未绑定的方法引用转换为 Consumer
,而当我显式传递 Function
时它却不这样做?
这里有两点,lambda 表达式是多边形表达式 - 它们由编译器使用其上下文(例如泛型)推断。
当您声明 consume(Holder::getHolded);
时,编译器(根据所谓的 特殊无效兼容性规则 )会将其推断为 Consumer<Holder>
.
这可能看起来并不明显,但请考虑一个简化的示例。调用一个方法并丢弃它的 return 类型通常不是一件好事,对吧?例如:
List<Integer> list = new ArrayList<>();
list.add(1);
即使 list.add(1)
return 是一个布尔值,我们也不关心它。
因此你的例子可以简化为:
consume(x -> {
x.getHolded(); // ignore the result here
return;
});
因此这些都是可能且有效的声明:
Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;
但在这种情况下,我们显式告诉Holder::getHolded
是什么类型,这不是编译器推断的,因此consume(getHolded);
失败,一个Consumer
!= Function
毕竟。
Java 8 在包java.util.function
中引入了4个重要的"function shapes"。
- Consumer -> 接受一个方法引用(或 lambda 表达式),它接受一个参数但不 return 任何东西
- Supplier -> 接受不带参数的方法引用(或 lambda 表达式)并且 return 是一个对象。
- Function -> 接受一个带有一个参数的方法引用(或 lambda 表达式),return 是一个对象。
- Predicate -> 接受一个带有一个参数的方法引用(或 lambda 表达式),return 是一个布尔值。
阅读 Java docs 了解更多详情。
要回答您为什么第一个有效但第二个出错的问题,请阅读以下内容:
第二条语句
consume(getHolded);
不起作用,因为参数 getHolded
的类型是 Function<Holder, String>
而 consume
方法需要类型为 Consumer<Holder>
的参数。由于 Function
和 Consumer
之间没有父子关系,因此需要显式转换,否则编译器会正确地出错。
第一个语句
consume(Holder::getHolded);
之所以有效,是因为方法 getHolded
被声明为 public String getHolded()
,这意味着它不接受任何参数并且 return 是 String
。根据新的 void compatibility 规则,void 类型被推断为包含引用方法的 class。考虑以下语句:
Consumer<Holder> consumer = Holder::getHolded;
即使方法 getHolded
不接受任何参数,这也是一个有效的语句。这有助于推断 void 类型。还有一个例子是你自己提到的那个:
Function<Holder, String> getHolded = Holder::getHolded;
这也是一个有效的陈述,你说函数对象 getHolded
是一个 Function
并且 return 是 String
并且接受一个类型 Holder
即使分配的方法引用不带任何参数。
我正在学习 Java 8 中的方法参考,但我很难理解它为什么有效?
class Holder {
private String holded;
public Holder(String holded) {
this.holded = holded;
}
public String getHolded() {
return holded;
}
}
private void run() {
Function<Holder, String> getHolded = Holder::getHolded;
consume(Holder::getHolded); //This is correct...
consume(getHolded); //...but this is not
}
private void consume(Consumer<Holder> consumer) {
consumer.accept(null);
}
正如您在 run
方法中看到的那样 - Holder::getHolded
returns 未绑定方法引用,您可以通过将类型 Holder
的对象作为参数传递来调用它。像这样:getHolded.apply(holder)
但是为什么它在直接作为方法参数调用时将这个未绑定的方法引用转换为 Consumer
,而当我显式传递 Function
时它却不这样做?
这里有两点,lambda 表达式是多边形表达式 - 它们由编译器使用其上下文(例如泛型)推断。
当您声明 consume(Holder::getHolded);
时,编译器(根据所谓的 特殊无效兼容性规则 )会将其推断为 Consumer<Holder>
.
这可能看起来并不明显,但请考虑一个简化的示例。调用一个方法并丢弃它的 return 类型通常不是一件好事,对吧?例如:
List<Integer> list = new ArrayList<>();
list.add(1);
即使 list.add(1)
return 是一个布尔值,我们也不关心它。
因此你的例子可以简化为:
consume(x -> {
x.getHolded(); // ignore the result here
return;
});
因此这些都是可能且有效的声明:
Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;
但在这种情况下,我们显式告诉Holder::getHolded
是什么类型,这不是编译器推断的,因此consume(getHolded);
失败,一个Consumer
!= Function
毕竟。
Java 8 在包java.util.function
中引入了4个重要的"function shapes"。
- Consumer -> 接受一个方法引用(或 lambda 表达式),它接受一个参数但不 return 任何东西
- Supplier -> 接受不带参数的方法引用(或 lambda 表达式)并且 return 是一个对象。
- Function -> 接受一个带有一个参数的方法引用(或 lambda 表达式),return 是一个对象。
- Predicate -> 接受一个带有一个参数的方法引用(或 lambda 表达式),return 是一个布尔值。
阅读 Java docs 了解更多详情。
要回答您为什么第一个有效但第二个出错的问题,请阅读以下内容:
第二条语句
consume(getHolded);
不起作用,因为参数 getHolded
的类型是 Function<Holder, String>
而 consume
方法需要类型为 Consumer<Holder>
的参数。由于 Function
和 Consumer
之间没有父子关系,因此需要显式转换,否则编译器会正确地出错。
第一个语句
consume(Holder::getHolded);
之所以有效,是因为方法 getHolded
被声明为 public String getHolded()
,这意味着它不接受任何参数并且 return 是 String
。根据新的 void compatibility 规则,void 类型被推断为包含引用方法的 class。考虑以下语句:
Consumer<Holder> consumer = Holder::getHolded;
即使方法 getHolded
不接受任何参数,这也是一个有效的语句。这有助于推断 void 类型。还有一个例子是你自己提到的那个:
Function<Holder, String> getHolded = Holder::getHolded;
这也是一个有效的陈述,你说函数对象 getHolded
是一个 Function
并且 return 是 String
并且接受一个类型 Holder
即使分配的方法引用不带任何参数。