【编者按】本文作者为拥有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方法引用使用指南的更多相关文章

  1. Java 8 新特性-菜鸟教程 (2) -Java 8 方法引用

    Java 8 方法引用 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少冗余代码. 方法引用使用一对冒号 :: . 下面,我们在 Car 类中定义了 4 个方法作为例子 ...

  2. JAVA 8 方法引用 - Method References

    什么是方法引用 简单地说,就是一个Lambda表达式.在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对 ...

  3. Java 8 方法引用

    转自:https://www.runoob.com/java/java8-method-references.html 方法引用通过方法的名字来指向一个方法. 方法引用可以使语言的构造更紧凑简洁,减少 ...

  4. Java 之 方法引用

    方法引用 一.冗余的Lambda场景 来看一个简单的函数式接口以应用Lambda表达式: @FunctionalInterface public interface Printable { void ...

  5. Java(43)JDK新特性之方法引用

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228461.html 博客主页:https://www.cnblogs.com/testero ...

  6. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  7. Java 8函数编程轻松入门(四)方法引用

    C#中系统提供了许多IEnumerable的扩展方法.同样在Java 8中新引入了Collector类. 1.方法引用 定义: 简而言之:就是一个Lambda表达式.在Java 8中,我们我们会使用L ...

  8. Java笔记——Java8特性之Lambda、方法引用和Streams

    Java8已经推出了好一段时间了,而掌握Java8的新特性也是必要的,如果要进行Spring开发,那么可以发现Spring的官网已经全部使用Java8来编写示例代码了,所以,不学就看不懂. 这里涉及三 ...

  9. Java:方法的参数是传值还是传引用

    Java中方法的参数总是采用传值的方式. 下列方法欲实现对象的交换,但实际上是不能实现的. public void swap(simpleClass a,simpleClass b){ simpleC ...

随机推荐

  1. C# 通过IEnumberable接口和IEnumerator接口实现泛型和非泛型自定义集合类型foreach功能

    IEnumerator和IEnumerable的作用 其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持foreach循环,至于foreach循环,如 ...

  2. 【数组】Spiral Matrix II

    题目: Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. ...

  3. Cloudera Manager安装之利用parcels方式安装3或4节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(CentOS6.5)(五)

    参考博客 Cloudera Manager安装之利用parcels方式安装单节点集群  Cloudera Manager安装之Cloudera Manager 5.3.X安装(三)(tar方式.rpm ...

  4. C51单片机中data、idata、xdata、pdata的区别

    C51单片机中data.idata.xdata.pdata的区别 data: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小. idata: 固定指前 ...

  5. @Override 注解compiler1.5和compiler1.6不同

    说到注解问题,@interface 来定义注解类 这个注解出现是在jdk1.5版本出现. jdk1.5只支持@override对类的继承中方法重写的使用,不支持接口实现中方法重写的使用(这种情况下会报 ...

  6. xgboost使用

    xgboost的实现方式为多颗CART树,其实xgboost就是类似于随机森林,但是与随机森林不同,他不是多个子树决策的结果,CART树最后会算出一个得分,是一个值,最后算出分类的时候,是多个值结合在 ...

  7. libnetwork插件化网络功能

    Docker把网络跟存储这两部分的功能实现都以插件化形式剥离出来,允许用户通过指令来选择不同的后端实现.这也是Docker希望构建围绕着容器的强大生态系统的一些积极的尝试.剥离出来的独立容器网络项目叫 ...

  8. Nodejs学习笔记(十一)—数据采集器示例(request和cheerio)

    写在之前 很多人都有做数据采集的需求,用不同的语言,不同的方式都能实现,我以前也用C#写过,主要还是发送各类请求和正则解析数据比较繁琐些,总体来说没啥不好的,就是效率要差一些, 用nodejs写采集程 ...

  9. java.util.Collection源码分析和深度讲解

    写在开头 java.util.Collection 作为Java开发最常用的接口之一,我们经常使用,今天我带大家一起研究一下Collection接口,希望对大家以后的编程以及系统设计能有所帮助,本文所 ...

  10. Java 和 JSP 实现网站访问量统计 (刷新过滤)

    java 和 JSP 实现的统计网站访问量,不需要数据库,将数据存储在 指定位置的 txt 文件中,代码块分为两部分 首先, java 部分: import java.io.File; import ...