Java 8方法引用使用指南
【编者按】本文作者为拥有15年 Java 开发经验的资深程序员 Per-Åke Minborg,主要介绍如何灵活地解析 Java 中的方法引用。文章系国内 ITOM 管理平台 OneAPM 编译呈现。
方法引用
众所周知,在Java 8中我们可以使用方法引用。譬如,在我们需要遍历流元素时,可以使用 String::isEmpty 来引用isEmpty方法。试看下面这段代码:
Stream.of("A", "", "B").filter(Stream::isEmpty).count();
运行的结果为1(因为在这个流中只有一个空元素)。但是,如果我们要过滤出非空字符串,我们得写成.filter(s -> !s.isEmpty())。这是一个Lambda表达式。显然,这儿有个讨厌的不对称想象。我们可以使用方法引用,但却不能用它的反式。我们可以写predicate.negate()却不能写Stream::isEmpty.negate()或!Stream::isEmpty。
为什么呢?这是因为方法引用并非Lambda表达式或者函数接口。不过,使用Java的类型推导可以将方法引用解析为一个或多个函数接口。上例中的String::isEmpty至少可以解析为:
Predicate<String>
Function<String, Boolean>
所以,我们要排除其他可能性,确定到底将方法引用转换为哪个函数接口。本文在一定程度上解决了该问题。文中的代码来自开源项目Speedment,它让数据库看起来像Java 8的流。
解析方法引用
其实,以静态方法为“管道”,可以部分地解决这个问题——该静态方法以一个方法引用为输入,以特定的函数接口为其返回。试考虑下面这个简短的静态方法:
public static <T> Predicate<T> as(Predicate<T> predicate) {
return predicate;
}
现在,如果静态地导入这个方法,事实上,我们就能更简单地使用方法引用。如下例所示:
Stream.of("A", "", "B").filter(as(String::isEmpty).negate()).count();
这段代码返回的结果为2,即流中非空元素的数量。有关方法引用的使用方式,我们又向前迈进了一步。另一个好处是,这个解决方案让我们更轻松地编写predicates接口,例如:
.filter(as(String::isEmpty).negate().and("A"::equals))
解析所有方法引用
但是,现在仍有一个问题亟待解决。我们不能随随便便地创建一大堆静态as()函数,因为一个方法引用可能解析为多种as()方法,正如本文开头提到的那样。所以,一个更妙的解决方案,是把函数接口类型名添加至每个静态方法,这样我们就可以程序化地为每个函数接口转换方法选择一个特定的方法引用。我们有一个工具类,可以让每个方法引用都转换为Java标准包 `java.util.function中任意匹配的函数接口。
直接在GitHub下载最新版本
import java.util.function.*;
/**
*
* @author Per Minborg
*/
public class FunctionCastUtil {
public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
return biConsumer;
}
public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
return biFunction;
}
public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
return binaryOperator;
}
public static <T, U> BiPredicate<T, U> asBiPredicate(BiPredicate<T, U> biPredicate) {
return biPredicate;
}
public static BooleanSupplier asBooleanSupplier(BooleanSupplier booleanSupplier) {
return booleanSupplier;
}
public static <T> Consumer<T> asConsumer(Consumer<T> consumer) {
return consumer;
}
public static DoubleBinaryOperator asDoubleBinaryOperator(DoubleBinaryOperator doubleBinaryOperator) {
return doubleBinaryOperator;
}
public static DoubleConsumer asDoubleConsumer(DoubleConsumer doubleConsumer) {
return doubleConsumer;
}
public static <R> DoubleFunction<R> asDoubleFunction(DoubleFunction<R> doubleFunction) {
return doubleFunction;
}
public static DoublePredicate asDoublePredicate(DoublePredicate doublePredicate) {
return doublePredicate;
}
public static DoubleToIntFunction asDoubleToIntFunction(DoubleToIntFunction doubleToIntFunctiontem) {
return doubleToIntFunctiontem;
}
public static DoubleToLongFunction asDoubleToLongFunction(DoubleToLongFunction doubleToLongFunction) {
return doubleToLongFunction;
}
public static DoubleUnaryOperator asDoubleUnaryOperator(DoubleUnaryOperator doubleUnaryOperator) {
return doubleUnaryOperator;
}
public static <T, R> Function<T, R> asFunction(Function<T, R> function) {
return function;
}
public static IntBinaryOperator asIntBinaryOperator(IntBinaryOperator intBinaryOperator) {
return intBinaryOperator;
}
public static IntConsumer asIntConsumer(IntConsumer intConsumer) {
return intConsumer;
}
public static <R> IntFunction<R> asIntFunction(IntFunction<R> intFunction) {
return intFunction;
}
public static IntPredicate asIntPredicate(IntPredicate intPredicate) {
return intPredicate;
}
public static IntSupplier asIntSupplier(IntSupplier intSupplier) {
return intSupplier;
}
public static IntToDoubleFunction asIntToDoubleFunction(IntToDoubleFunction intToDoubleFunction) {
return intToDoubleFunction;
}
public static IntToLongFunction asIntToLongFunction(IntToLongFunction intToLongFunction) {
return intToLongFunction;
}
public static IntUnaryOperator asIntUnaryOperator(IntUnaryOperator intUnaryOperator) {
return intUnaryOperator;
}
public static LongBinaryOperator asLongBinaryOperator(LongBinaryOperator longBinaryOperator) {
return longBinaryOperator;
}
public static LongConsumer asLongConsumer(LongConsumer longConsumer) {
return longConsumer;
}
public static <R> LongFunction<R> asLongFunction(LongFunction<R> longFunction) {
return longFunction;
}
public static LongPredicate asLongPredicate(LongPredicate longPredicate) {
return longPredicate;
}
public static <T> LongSupplier asLongSupplier(LongSupplier longSupplier) {
return longSupplier;
}
public static LongToDoubleFunction asLongToDoubleFunction(LongToDoubleFunction longToDoubleFunction) {
return longToDoubleFunction;
}
public static LongToIntFunction asLongToIntFunction(LongToIntFunction longToIntFunction) {
return longToIntFunction;
}
public static LongUnaryOperator asLongUnaryOperator(LongUnaryOperator longUnaryOperator) {
return longUnaryOperator;
}
public static <T> ObjDoubleConsumer<T> asObjDoubleConsumer(ObjDoubleConsumer<T> objDoubleConsumer) {
return objDoubleConsumer;
}
public static <T> ObjIntConsumer<T> asObjIntConsumer(ObjIntConsumer<T> objIntConsumer) {
return objIntConsumer;
}
public static <T> ObjLongConsumer<T> asObjLongConsumer(ObjLongConsumer<T> objLongConsumer) {
return objLongConsumer;
}
public static <T> Predicate<T> asPredicate(Predicate<T> predicate) {
return predicate;
}
public static <T> Supplier<T> asSupplier(Supplier<T> supplier) {
return supplier;
}
public static <T, U> ToDoubleBiFunction<T, U> asToDoubleBiFunction(ToDoubleBiFunction<T, U> toDoubleBiFunction) {
return toDoubleBiFunction;
}
public static <T> ToDoubleFunction<T> asToDoubleFunction(ToDoubleFunction<T> toDoubleFunction) {
return toDoubleFunction;
}
public static <T, U> ToIntBiFunction<T, U> asToIntBiFunction(ToIntBiFunction<T, U> toIntBiFunction) {
return toIntBiFunction;
}
public static <T> ToIntFunction<T> asToIntFunction(ToIntFunction<T> ioIntFunction) {
return ioIntFunction;
}
public static <T, U> ToLongBiFunction<T, U> asToLongBiFunction(ToLongBiFunction<T, U> toLongBiFunction) {
return toLongBiFunction;
}
public static <T> ToLongFunction<T> asToLongFunction(ToLongFunction<T> toLongFunction) {
return toLongFunction;
}
public static <T> UnaryOperator<T> asUnaryOperator(UnaryOperator<T> unaryOperator) {
return unaryOperator;
}
private FunctionCastUtil() {
}
}
在静态导入了相关方法之后,我们就可以这样写:
Stream.of("A", "", "B").filter(asPredicate(String::isEmpty).negate()).count();
一个更好的解决方案
如果函数接口本身就包含一个接收方法引用并将其转换为某类函数接口的静态方法,那就更好了。举例来说,标准的Java Predicated函数接口就会变成这样:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {...}
default Predicate<T> negate() {...}
default Predicate<T> or(Predicate<? super T> other) {...}
static <T> Predicate<T> isEqual(Object targetRef) {...}
// New proposed support method to return a
// Predicate view of a Functional Reference
public static <T> Predicate<T> of(Predicate<T> predicate) {
return predicate;
}
}
因此,我们可以这样写:
Stream.of("A", "", "B").filter(Predicate.of(String::isEmpty).negate()).count();
笔者觉得这样看起来好极了!
快联系离你最近的Open JDK开发人员,提出你的修改建议吧!
OneAPM 能为您提供端到端的 Java 应用性能解决方案,我们支持所有常见的 Java 框架及应用服务器,助您快速发现系统瓶颈,定位异常根本原因。分钟级部署,即刻体验,Java 监控从来没有如此简单。想阅读更多技术文章,请访问 OneAPM 官方技术博客。
本文转自 OneAPM 官方博客
原帖地址:https://dzone.com/articles/put-your-java-8-method-references-to-work
Java 8方法引用使用指南的更多相关文章
- Java 8 新特性-菜鸟教程 (2) -Java 8 方法引用
Java 8 方法引用 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少冗余代码. 方法引用使用一对冒号 :: . 下面,我们在 Car 类中定义了 4 个方法作为例子 ...
- JAVA 8 方法引用 - Method References
什么是方法引用 简单地说,就是一个Lambda表达式.在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对 ...
- Java 8 方法引用
转自:https://www.runoob.com/java/java8-method-references.html 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少 ...
- Java 之 方法引用
方法引用 一.冗余的Lambda场景 来看一个简单的函数式接口以应用Lambda表达式: @FunctionalInterface public interface Printable { void ...
- Java(43)JDK新特性之方法引用
作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228461.html 博客主页:https://www.cnblogs.com/testero ...
- 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...
- Java 8函数编程轻松入门(四)方法引用
C#中系统提供了许多IEnumerable的扩展方法.同样在Java 8中新引入了Collector类. 1.方法引用 定义: 简而言之:就是一个Lambda表达式.在Java 8中,我们我们会使用L ...
- Java笔记——Java8特性之Lambda、方法引用和Streams
Java8已经推出了好一段时间了,而掌握Java8的新特性也是必要的,如果要进行Spring开发,那么可以发现Spring的官网已经全部使用Java8来编写示例代码了,所以,不学就看不懂. 这里涉及三 ...
- Java:方法的参数是传值还是传引用
Java中方法的参数总是采用传值的方式. 下列方法欲实现对象的交换,但实际上是不能实现的. public void swap(simpleClass a,simpleClass b){ simpleC ...
随机推荐
- express中间件原理 && 实现
一.什么是express中间件? 什么是express中间件呢? 我们肯定都听说过这个词,并且,如果你用过express,那么你就一定用过express中间件,如下: var express = re ...
- PHP 批量获取 百度搜索结果 网址列表
<?php set_time_limit(0); function curl($url){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $u ...
- Mina的服务器
(一) package testMina; import java.io.IOException; import java.net.InetSocketAddress; import java.nio ...
- 浅谈Android Studio中项目结构中project模式的各个文件和文件夹
致敬郭霖,这些知识是从第一行代码第二版中直接码下来的,谢谢他,注意每个条目前是否有. 1..gradle和.idea 这两个目录下放置的都是Android Studio自动生成的一些文件,我们无需关心 ...
- fine ui使用笔记
1.top.X.alert("保存成功"); 2.Alert.GetShowInTopReference("这是在服务器端生成的客户端事件"); 注明:1与2等 ...
- PMP备考指南之第二章:项目运作环境
本文已同步至 GitHub/Gitee/公众号,感兴趣的同学帮忙点波关注~ 第二章:项目运作环境 1. 事业环境因素.组织过程资产 事业环境因素 Enterprise Environmental Fa ...
- HMM分词实例
class HMM(object): def __init__(self): import os # 主要是用于存取算法中间结果,不用每次都训练模型 self.model_file = 'model/ ...
- 电信固定ip宽带80与8080端口踩坑
本文只是作为记录,避免后面遇到此类问题耗费时间. 实际情况:公司有个固定电信宽带是固定IP的,想把固定IP映射到测试环境ip,实现可以公网通过固定ip访问,内网通过局域网ip访问. 测试环境服务是占用 ...
- springboot-mongodb的多数据源配置
pom.xml中引入mongodb的依赖 <dependency> <groupId>org.springframework.boot</groupId> < ...
- vue 2.x之组件的数据传递(一)
这是根据官方提供的脚手架vue-cli搭建,通过简单的案例来介绍vue数据的传递的方式,根据自己平时用到的,来做简单的总结: 1.父组件传递数据给子组件 父组件传递数据给子组件,需要把子组件引入,并挂 ...